การส่งสองพารามิเตอร์คำสั่งโดยใช้การเชื่อม WPF


155

ฉันมีคำสั่งที่ฉันเรียกใช้งานจากไฟล์ XAML ของฉันโดยใช้ไวยากรณ์มาตรฐานต่อไปนี้:

<Button Content="Zoom" Command="{Binding MyViewModel.ZoomCommand}"/>

สิ่งนี้ใช้ได้ดีจนกระทั่งฉันรู้ว่าฉันต้องการข้อมูลสองชิ้นจากมุมมองเพื่อทำให้การดำเนินการเสร็จสมบูรณ์ตามที่ผู้ใช้คาดหวัง (ความกว้างและความสูงของผืนผ้าใบพิเศษ)

ดูเหมือนว่าเป็นไปได้ที่จะส่งอาเรย์เป็นอาร์กิวเมนต์ไปยังคำสั่งของฉัน แต่ฉันไม่เห็นว่ามีวิธีการระบุการโยงกับคุณสมบัติ Canvas ทั้งสองของฉันใน CommandParameter:

<Button Content="Zoom" 
        Command="{Binding MyViewModel.ZoomCommand" 
        CommandParameter="{Binding ElementName=MyCanvas, Path=Width}"/>

ฉันจะส่งทั้งความกว้างและความสูงไปยังคำสั่งของฉันได้อย่างไร ดูเหมือนว่ามันจะเป็นไปไม่ได้หากใช้คำสั่งจาก XAML และฉันต้องวางสายจัดการตัวคลิกในโค้ดของฉันเพื่อรับข้อมูลนี้เพื่อส่งต่อไปยังวิธีการซูมของฉัน


[ stackoverflow.com/questions/58114752/…ทางออกข้างต้น ฉันมีปัญหาเดียวกัน)
user1482689

คำตอบ:


240

ประการแรกหากคุณกำลังทำ MVVM โดยทั่วไปคุณจะต้องให้ข้อมูลนี้แก่ VM ของคุณผ่านคุณสมบัติที่แยกต่างหากซึ่งเชื่อมโยงกับมุมมอง ที่ช่วยให้คุณไม่ต้องส่งพารามิเตอร์ใด ๆ เลยไปยังคำสั่งของคุณ

อย่างไรก็ตามคุณสามารถเชื่อมโยงหลายตัวและใช้ตัวแปลงเพื่อสร้างพารามิเตอร์:

<Button Content="Zoom" Command="{Binding MyViewModel.ZoomCommand">
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource YourConverter}">
             <Binding Path="Width" ElementName="MyCanvas"/>
             <Binding Path="Height" ElementName="MyCanvas"/>
        </MultiBinding>
    </Button.CommandParameter>
</Button>

ในตัวแปลงของคุณ:

public class YourConverter : IMultiValueConverter
{
    public object Convert(object[] values, ...)
    {
        return values.Clone();
    }

    ...
}

จากนั้นในตรรกะการดำเนินการคำสั่งของคุณ:

public void OnExecute(object parameter)
{
    var values = (object[])parameter;
    var width = (double)values[0];
    var height = (double)values[1];
}

1
ขอบคุณเคน - นั่นคือสิ่งที่ฉันกำลังมองหา ฉันชอบวิธีแรกของคุณดีขึ้นเพื่อให้ VM รู้ "สถานะ" ของมุมมองผ่านการเชื่อมโยงโดยที่ฉันไม่ต้องผ่านพารามิเตอร์เลย แต่ฉันยังสามารถทดสอบได้ ฉันไม่แน่ใจว่าจะทำงานให้ฉันที่นี่เพราะฉันต้องการมุมมองที่จะทำให้ผืนผ้าใบมีขนาดใหญ่ที่สุดเท่าที่จะเป็นไปได้และส่งผ่านค่านี้ไปยัง VM ถ้าฉันผูกมันฉันจะต้องตั้งค่าความกว้างใน VM หรือไม่? ในกรณีใด VM ถูกผูกไว้กับมุมมอง?
JasonD

@ Jason: คุณสามารถทำได้ทั้งสองทาง กล่าวคือให้เปลี่ยนมุมมองการเปลี่ยนกลับเป็นโมเดลมุมมองหรือให้โมเดลการเปลี่ยนแปลงมุมมองเปลี่ยนเป็นมุมมอง การผูกสองทางจะส่งผลให้คุณมีตัวเลือกอย่างใดอย่างหนึ่ง
Kent Boogaart

ในโปรแกรมวิธีพารามิเตอร์ OnExecute ของฉันเป็นอาร์เรย์ที่มีค่า Null แต่ในตัวแปลงค่าจะเป็นไปตามที่คาดไว้
Alex David

2
ฉันพบว่าพารามิเตอร์เป็นโมฆะในวิธี OnExecute ยัง YourConverter.Convert () ไม่ได้ถูกเรียกหลังจากคลิกปุ่ม ทำไม?
SubmarineX

3
สิ่งนี้ไม่ทำงานเมื่อกดปุ่มพารามิเตอร์จะเป็นโมฆะ
adminSoftDK

38

ในตัวแปลงของโซลูชันที่เลือกคุณควรเพิ่มค่า Clone () มิฉะนั้นพารามิเตอร์ในคำสั่งสิ้นสุด null

public class YourConverter : IMultiValueConverter
{
    public object Convert(object[] values, ...)
    {
        return values.Clone();
    }

    ...
}

6
สวัสดีการเพิ่มด้วย Clone () ทำให้ใช้งานได้ :) คุณช่วยอธิบายได้ไหมว่ามันต่างกันอย่างไร เพราะฉันไม่เข้าใจว่าทำไม Clone () ถึงต้องทำงาน ขอบคุณ.
adminSoftDK

ฉันอาจจะผิด แต่สิ่งนี้ (บรรทัดที่ 1267) ดูเหมือนว่าอาจเป็นเหตุผลสำหรับฉัน: Referencesource.microsoft.com/#PresentationFramework/src/…
maxp

14

ใช้ Tuple ในตัวแปลงและใน OnExecute ส่งวัตถุพารามิเตอร์กลับไปที่ Tuple

public class YourConverter : IMultiValueConverter 
{      
    public object Convert(object[] values, ...)     
    {   
        Tuple<string, string> tuple = new Tuple<string, string>(
            (string)values[0], (string)values[1]);
        return (object)tuple;
    }      
} 

// ...

public void OnExecute(object parameter) 
{
    var param = (Tuple<string, string>) parameter;
}

5

หากค่าของคุณเป็นแบบคงที่คุณสามารถใช้x:Array:

<Button Command="{Binding MyCommand}">10
  <Button.CommandParameter>
    <x:Array Type="system:Object">
       <system:String>Y</system:String>
       <system:Double>10</system:Double>
    </x:Array>
  </Button.CommandParameter>
</Button>

" ถ้าค่าของคุณเป็นแบบคงที่ ": ทรัพยากรแบบคงที่คืออะไร? ตัวอย่างเช่นคำถามที่กล่าวถึงความกว้างและความสูงของผ้าใบ ค่าเหล่านี้ไม่คงที่ แต่เป็นค่าคงที่หรือไม่ อะไรคือ XAML ในกรณีนี้
นาที

2
ฉันควรจะเขียน "คงที่" แทน "คงที่" ทรัพยากรคงที่เป็นทรัพยากรที่ไม่เปลี่ยนแปลงระหว่างการดำเนินการ หากคุณใช้SystemColorsตัวอย่างเช่นคุณควรใช้DynamicResourceแทนStaticResourceเพราะผู้ใช้สามารถเปลี่ยนสีของระบบผ่านแผงควบคุมในระหว่างการดำเนินการ Canvas WidthและHeightไม่ใช่ทรัพยากรและไม่คงที่ FrameworkElementมีคุณสมบัติเช่นรับมรดกมาจากมี
Maxence

2

เกี่ยวกับการใช้ Tuple ใน Converter มันจะดีกว่าถ้าใช้ 'object' แทน 'string' เพื่อให้มันใช้ได้กับวัตถุทุกประเภทโดยไม่มีข้อ จำกัด ของวัตถุ 'string'

public class YourConverter : IMultiValueConverter 
{      
    public object Convert(object[] values, ...)     
    {   
        Tuple<object, object> tuple = new Tuple<object, object>(values[0], values[1]);
        return tuple;
    }      
} 

ตรรกะการดำเนินการใน Command อาจเป็นเช่นนี้

public void OnExecute(object parameter) 
{
    var param = (Tuple<object, object>) parameter;

    // e.g. for two TextBox object
    var txtZip = (System.Windows.Controls.TextBox)param.Item1;
    var txtCity = (System.Windows.Controls.TextBox)param.Item2;
}

และ multi-bind ด้วยตัวแปลงเพื่อสร้างพารามิเตอร์ (ด้วยวัตถุกล่องข้อความสอง)

<Button Content="Zip/City paste" Command="{Binding PasteClick}" >
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource YourConvert}">
            <Binding ElementName="txtZip"/>
            <Binding ElementName="txtCity"/>
        </MultiBinding>
    </Button.CommandParameter>
</Button>

ฉันชอบอันนี้เนื่องจากมันชัดเจนว่ามีกี่พารามิเตอร์ที่ตัวแปลงรองรับ ดีเพียงสองพารามิเตอร์! (ยิ่งคุณแสดง XAML และฟังก์ชั่นสั่งการสำหรับการครอบคลุมทั้งหมด)
Caleb W.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.