การเริ่มเหตุการณ์ดับเบิลคลิกจากรายการ WPF ListView โดยใช้ MVVM


102

ในแอปพลิเคชัน WPF โดยใช้ MVVM ฉันมี usercontrol พร้อมรายการมุมมองรายการ ในขณะทำงานจะใช้ databinding เพื่อเติมเต็ม listview ด้วยคอลเล็กชันของวัตถุ

อะไรคือวิธีที่ถูกต้องในการแนบเหตุการณ์ดับเบิลคลิกเข้ากับรายการในมุมมองรายการเพื่อที่เมื่อรายการในมุมมองรายการถูกดับเบิลคลิกเหตุการณ์ที่เกี่ยวข้องในโมเดลมุมมองจะเริ่มทำงานและมีการอ้างอิงถึงรายการที่คลิก

มันจะทำได้อย่างไรด้วยวิธี MVVM ที่สะอาดเช่นไม่มีรหัสอยู่ข้างหลังใน View?

คำตอบ:


76

กรุณารหัสที่อยู่เบื้องหลังไม่ใช่สิ่งเลวร้าย แต่อย่างใด น่าเสียดายที่ผู้คนจำนวนมากในชุมชน WPF เข้าใจผิด

MVVM ไม่ใช่รูปแบบในการกำจัดโค้ดที่อยู่เบื้องหลัง เป็นการแยกส่วนมุมมอง (ลักษณะภาพเคลื่อนไหว ฯลฯ ) ออกจากส่วนลอจิก (เวิร์กโฟลว์) นอกจากนี้คุณยังสามารถทดสอบหน่วยส่วนตรรกะได้

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

ตัวอย่างแอปพลิเคชันที่ใช้โค้ดหลังและยังคงใช้การแยก MVVM ได้ที่นี่:

WPF Application Framework (WAF) - http://waf.codeplex.com


5
พูดดีฉันปฏิเสธที่จะใช้รหัสทั้งหมดนั้นและ DLL พิเศษเพียงแค่ดับเบิลคลิก!
Eduardo Molteni

4
การใช้การผูกสิ่งนี้ทำให้ฉันปวดหัวจริงๆ เหมือนกับการถูกขอรหัสด้วยแขน 1 ข้างผ้าปิดตา 1 ข้างและยืนบนขา 1 ข้าง การดับเบิลคลิกควรจะง่ายและฉันไม่เห็นว่าโค้ดเพิ่มเติมทั้งหมดนี้คุ้มค่าแค่ไหน
Echiban

1
ฉันเกรงว่าจะไม่เห็นด้วยกับคุณทั้งหมด ถ้าคุณพูดว่า 'code behind is not bad' ฉันมีคำถามเกี่ยวกับเรื่องนี้: ทำไมเราไม่มอบหมายเหตุการณ์การคลิกสำหรับปุ่ม แต่มักจะใช้การผูก (โดยใช้คุณสมบัติ Command) แทน
Nam G VU

21
@ Nam Gi VU: ฉันมักจะชอบ Command Binding เมื่อได้รับการสนับสนุนโดย WPF Control การผูกคำสั่งทำมากกว่าแค่การถ่ายทอดเหตุการณ์ 'คลิก' ไปยัง ViewModel (เช่น CanExecute) แต่คำสั่งจะพร้อมใช้งานสำหรับสถานการณ์ที่พบบ่อยที่สุดเท่านั้น สำหรับสถานการณ์อื่น ๆ เราสามารถใช้ไฟล์ code-behind และที่นั่นเราได้มอบหมายข้อกังวลที่ไม่เกี่ยวข้องกับ UI ให้กับ ViewModel หรือ Model
jbe

2
ตอนนี้ฉันเข้าใจคุณมากขึ้น! พูดคุยกับคุณได้ดี!
Nam G VU

73

ฉันสามารถทำให้สิ่งนี้ทำงานกับ. NET 4.5 ได้ ดูเหมือนตรงไปตรงมาและไม่จำเป็นต้องมีบุคคลที่สามหรือรหัสที่อยู่เบื้องหลัง

<ListView ItemsSource="{Binding Data}">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid Margin="2">
                    <Grid.InputBindings>
                        <MouseBinding Gesture="LeftDoubleClick" Command="{Binding ShowDetailCommand}"/>
                    </Grid.InputBindings>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Image Source="..\images\48.png" Width="48" Height="48"/>
                    <TextBlock Grid.Row="1" Text="{Binding Name}" />
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

2
ดูเหมือนจะใช้ไม่ได้กับพื้นที่ทั้งหมดเช่นฉันทำสิ่งนี้บนแผงเชื่อมต่อและใช้งานได้เฉพาะในกรณีที่มีบางอย่างอยู่ในแผงท่าเรือ (เช่นบล็อกข้อความรูปภาพ) แต่ไม่ใช่พื้นที่ว่าง
Stephen Drew

3
ตกลง - เกาลัดเก่านี้อีกแล้ว ... ต้องตั้งพื้นหลังให้โปร่งใสเพื่อรับเหตุการณ์เมาส์ตามstackoverflow.com/questions/7991314/…
Stephen Drew

6
ฉันเกาหัวของฉันพยายามคิดว่าทำไมมันถึงได้ผลสำหรับพวกคุณทุกคนไม่ใช่สำหรับฉัน ทันใดนั้นฉันก็ตระหนักว่าภายในบริบทของเทมเพลตรายการบริบทข้อมูลคือรายการปัจจุบันจากแหล่งข้อมูลไม่ใช่แบบจำลองมุมมองของหน้าต่างหลัก ดังนั้นฉันจึงใช้สิ่งต่อไปนี้เพื่อให้มันทำงาน <MouseBinding MouseAction = "LeftDoubleClick" Command = "{Binding Path = DataContext.EditBandCommand, RelativeSource = {RelativeSource AncestorType = {x: Type Window}}}" /> ในกรณีของฉัน EditBandCommand คือ คำสั่งบน viewmodel ของเพจไม่ได้อยู่บนเอนทิตีที่ถูกผูกไว้
naskew

naskew มีซอสลับที่ฉันต้องการด้วย MVVM Light โดยได้รับพารามิเตอร์คำสั่งเป็นโมเดลอ็อบเจ็กต์ใน listboxitem ที่คลิกสองครั้งและบริบทข้อมูลของหน้าต่างถูกตั้งค่าเป็นโมเดลมุมมองที่แสดงคำสั่ง: <MouseBinding Gesture = "LeftDoubleClick "Command =" {Binding Path = DataContext.OpenSnapshotCommand, RelativeSource = {RelativeSource AncestorType = {x: Type Window}}} "CommandParameter =" {Binding} "/>
MC5

เพียงแค่ต้องการเพิ่มที่InputBindingsพร้อมใช้งานจาก. NET 3.0 และไม่มีใน Silverlight
Martin

44

ฉันชอบใช้Attached Command Behaviors and Commands Marlon Grechมีการนำพฤติกรรมคำสั่งที่แนบมาไปใช้งานได้ดีมาก เมื่อใช้สิ่งเหล่านี้เราสามารถกำหนดสไตล์ให้กับคุณสมบัติItemContainerStyleของ ListView ซึ่งจะตั้งค่าคำสั่งสำหรับแต่ละ ListViewItem

ที่นี่เราตั้งค่าคำสั่งที่จะยิงบนเหตุการณ์ MouseDoubleClick และ CommandParameter จะเป็นวัตถุข้อมูลที่เราคลิก ที่นี่ฉันกำลังเดินทางไปที่แผนผังภาพเพื่อรับคำสั่งที่ฉันใช้ แต่คุณสามารถสร้างคำสั่งกว้าง ๆ ของแอปพลิเคชันได้อย่างง่ายดาย

<Style x:Key="Local_OpenEntityStyle"
       TargetType="{x:Type ListViewItem}">
    <Setter Property="acb:CommandBehavior.Event"
            Value="MouseDoubleClick" />
    <Setter Property="acb:CommandBehavior.Command"
            Value="{Binding ElementName=uiEntityListDisplay, Path=DataContext.OpenEntityCommand}" />
    <Setter Property="acb:CommandBehavior.CommandParameter"
            Value="{Binding}" />
</Style>

สำหรับคำสั่งที่คุณสามารถดำเนินการICommandโดยตรงหรือใช้บางส่วนของผู้ช่วยเหลือเช่นผู้ที่มาในMVVM Toolkit


1
+1 ฉันพบว่านี่เป็นทางออกที่ฉันต้องการเมื่อทำงานกับ Composite Application Guidance สำหรับ WPF (Prism)
Travis Heseman

1
เนมสเปซ 'acb:' ย่อมาจากอะไรในตัวอย่างโค้ดของคุณ
Nam G VU

@NamGiVU acb:= AttachedCommandBehavior รหัสสามารถพบได้ในลิงค์แรกในคำตอบ
Rachel

ฉันลองแค่นั้นและได้รับข้อยกเว้นตัวชี้ null จากคลาส CommandBehaviorBinding บรรทัด 99 ตัวแปร "กลยุทธ์" เป็นโมฆะ เกิดอะไรขึ้น?
etwas77

13

ฉันพบวิธีที่ง่ายและสะอาดมากในการดำเนินการนี้ด้วยทริกเกอร์ Blend SDK Event ทำความสะอาด MVVM ใช้ซ้ำได้และไม่มีรหัสหลัง

คุณอาจมีสิ่งนี้อยู่แล้ว:

<Style x:Key="MyListStyle" TargetType="{x:Type ListViewItem}">

ตอนนี้รวม ControlTemplate สำหรับ ListViewItem เช่นนี้หากคุณยังไม่ได้ใช้:

<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type ListViewItem}">
      <GridViewRowPresenter Content="{TemplateBinding Content}"
                            Columns="{TemplateBinding GridView.ColumnCollection}" />
    </ControlTemplate>
  </Setter.Value>
 </Setter>

GridViewRowPresenter จะเป็นรากภาพขององค์ประกอบทั้งหมด "ภายใน" ซึ่งประกอบเป็นองค์ประกอบแถวรายการ ตอนนี้เราสามารถแทรกทริกเกอร์ที่นั่นเพื่อค้นหาเหตุการณ์ที่กำหนดเส้นทาง MouseDoubleClick และเรียกคำสั่งผ่าน InvokeCommandAction ดังนี้:

<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type ListViewItem}">
      <GridViewRowPresenter Content="{TemplateBinding Content}"
                            Columns="{TemplateBinding GridView.ColumnCollection}">
        <i:Interaction.Triggers>
          <i:EventTrigger EventName="MouseDoubleClick">
            <i:InvokeCommandAction Command="{Binding DoubleClickCommand}" />
          </i:EventTrigger>
        </i:Interaction.Triggers>
      </GridViewRowPresenter>
    </ControlTemplate>
  </Setter.Value>
 </Setter>

หากคุณมีองค์ประกอบภาพ "ด้านบน" GridRowPresenter (probalby เริ่มต้นด้วยเส้นตาราง) คุณสามารถวาง Trigger ไว้ที่นั่นได้

น่าเสียดายที่เหตุการณ์ MouseDoubleClick ไม่ได้สร้างขึ้นจากทุกองค์ประกอบภาพ (มาจาก Controls แต่ไม่ใช่จาก FrameworkElements เป็นต้น) วิธีแก้ปัญหาคือการได้รับคลาสจาก EventTrigger และมองหา MouseButtonEventArgs ด้วย ClickCount เป็น 2 สิ่งนี้จะกรองสิ่งที่ไม่ใช่ MouseButtonEvents และ MoseButtonEvents ทั้งหมดออกอย่างมีประสิทธิภาพด้วย ClickCount! = 2

class DoubleClickEventTrigger : EventTrigger
{
    protected override void OnEvent(EventArgs eventArgs)
    {
        var e = eventArgs as MouseButtonEventArgs;
        if (e == null)
        {
            return;
        }
        if (e.ClickCount == 2)
        {
            base.OnEvent(eventArgs);
        }
    }
}

ตอนนี้เราสามารถเขียนสิ่งนี้ได้แล้ว ('h' คือเนมสเปซของคลาสตัวช่วยด้านบน):

<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type ListViewItem}">
      <GridViewRowPresenter Content="{TemplateBinding Content}"
                            Columns="{TemplateBinding GridView.ColumnCollection}">
        <i:Interaction.Triggers>
          <h:DoubleClickEventTrigger EventName="MouseDown">
            <i:InvokeCommandAction Command="{Binding DoubleClickCommand}" />
          </h:DoubleClickEventTrigger>
        </i:Interaction.Triggers>
      </GridViewRowPresenter>
    </ControlTemplate>
  </Setter.Value>
 </Setter>

จากที่ฉันพบว่าคุณใส่ Trigger ลงบน GridViewRowPresenter โดยตรงหรือไม่อาจมีปัญหาได้ ช่องว่างระหว่างคอลัมน์อาจไม่ได้รับเหตุการณ์ของเมาส์เลย (อาจเป็นวิธีแก้ปัญหาชั่วคราวเพื่อจัดรูปแบบด้วยการยืดการจัดตำแหน่ง)
Gunter

ในกรณีนี้ควรวางตารางว่างรอบ ๆ GridViewRowPresenter แล้ววางทริกเกอร์ไว้ที่นั่น ดูเหมือนว่าจะได้ผล
Gunter

1
โปรดทราบว่าคุณสูญเสียสไตล์เริ่มต้นสำหรับ ListViewItem หากคุณแทนที่เทมเพลตเช่นนี้ มันไม่สำคัญสำหรับแอปพลิเคชันที่ฉันกำลังทำงานอยู่เนื่องจากมันใช้สไตล์ที่ปรับแต่งเองอย่างหนักอยู่แล้ว
Gunter

6

ฉันตระหนักดีว่าการสนทนานี้มีอายุหนึ่งปีแล้ว แต่ด้วย. NET 4 มีความคิดเกี่ยวกับวิธีแก้ปัญหานี้หรือไม่ ฉันเห็นด้วยอย่างยิ่งว่าประเด็นของ MVVM ไม่ใช่การกำจัดโค้ดที่อยู่เบื้องหลังไฟล์ ฉันรู้สึกเป็นอย่างยิ่งที่เพียงเพราะบางอย่างซับซ้อนไม่ได้หมายความว่าจะดีกว่า นี่คือสิ่งที่ฉันใส่รหัสไว้ด้านหลัง:

    private void ButtonClick(object sender, RoutedEventArgs e)
    {
        dynamic viewModel = DataContext;
        viewModel.ButtonClick(sender, e);
    }

12
คุณควรดูโมเดลควรมีชื่อที่แสดงถึงการกระทำที่คุณสามารถทำได้ในโดเมนของคุณ การดำเนินการ "ButtonClick" ในโดเมนของคุณคืออะไร ViewModel แสดงถึงตรรกะของโดเมนในบริบทที่เป็นมิตรกับมุมมองไม่ใช่แค่ผู้ช่วยของมุมมอง ดังนั้น: ButtonClick ไม่ควรอยู่ใน viewmodel ใช้ viewModel.DeleteSelectedCustomer หรืออะไรก็ตามที่การกระทำนี้แสดงถึงแทน
Marius

4

คุณสามารถใช้คุณสมบัติการดำเนินการของCaliburnเพื่อแมปเหตุการณ์กับเมธอดบน ViewModel ของคุณ สมมติว่าคุณมีItemActivatedวิธีการในViewModelXAML ที่เกี่ยวข้องจะมีลักษณะดังนี้:

<ListView x:Name="list" 
   Message.Attach="[Event MouseDoubleClick] = [Action ItemActivated(list.SelectedItem)]" >

สำหรับรายละเอียดเพิ่มเติมคุณสามารถตรวจสอบเอกสารและตัวอย่างของ Caliburn


4

ฉันพบว่าการเชื่อมโยงคำสั่งนั้นง่ายกว่าเมื่อสร้างมุมมอง:

var r = new MyView();
r.MouseDoubleClick += (s, ev) => ViewModel.MyCommand.Execute(null);
BindAndShow(r, ViewModel);

ในกรณีของฉันBindAndShowมีลักษณะเช่นนี้ (updatecontrols + avalondock):

private void BindAndShow(DockableContent view, object viewModel)
{
    view.DataContext = ForView.Wrap(viewModel);
    view.ShowAsDocument(dockManager);
    view.Focus();
}

แม้ว่าแนวทางนี้ควรใช้กับวิธีการใดก็ตามที่คุณมีในการเปิดมุมมองใหม่ ๆ


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

1

ฉันเห็นวิธีแก้ปัญหาจากrushuiด้วย InuptBindings แต่ฉันยังไม่สามารถเข้าถึงพื้นที่ของ ListViewItem ที่ไม่มีข้อความได้แม้ว่าจะตั้งค่าพื้นหลังเป็นแบบโปร่งใสแล้วก็ตามดังนั้นฉันจึงแก้ไขโดยใช้เทมเพลตที่แตกต่างกัน

เทมเพลตนี้มีไว้สำหรับเมื่อเลือก ListViewItem และใช้งานอยู่:

<ControlTemplate x:Key="SelectedActiveTemplate" TargetType="{x:Type ListViewItem}">
   <Border Background="LightBlue" HorizontalAlignment="Stretch">
   <!-- Bind the double click to a command in the parent view model -->
      <Border.InputBindings>
         <MouseBinding Gesture="LeftDoubleClick" 
                       Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ItemSelectedCommand}"
                       CommandParameter="{Binding}" />
      </Border.InputBindings>
      <TextBlock Text="{Binding TextToShow}" />
   </Border>
</ControlTemplate>

เทมเพลตนี้มีไว้สำหรับเมื่อเลือก ListViewItem แล้วและไม่ได้ใช้งาน:

<ControlTemplate x:Key="SelectedInactiveTemplate" TargetType="{x:Type ListViewItem}">
   <Border Background="Lavender" HorizontalAlignment="Stretch">
      <TextBlock Text="{Binding TextToShow}" />
   </Border>
</ControlTemplate>

นี่คือสไตล์เริ่มต้นที่ใช้สำหรับ ListViewItem:

<Style TargetType="{x:Type ListViewItem}">
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate>
            <Border HorizontalAlignment="Stretch">
               <TextBlock Text="{Binding TextToShow}" />
            </Border>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
   <Style.Triggers>
      <MultiTrigger>
         <MultiTrigger.Conditions>
            <Condition Property="IsSelected" Value="True" />
            <Condition Property="Selector.IsSelectionActive" Value="True" />
         </MultiTrigger.Conditions>
         <Setter Property="Template" Value="{StaticResource SelectedActiveTemplate}" />
      </MultiTrigger>
      <MultiTrigger>
         <MultiTrigger.Conditions>
            <Condition Property="IsSelected" Value="True" />
            <Condition Property="Selector.IsSelectionActive" Value="False" />
         </MultiTrigger.Conditions>
         <Setter Property="Template" Value="{StaticResource SelectedInactiveTemplate}" />
      </MultiTrigger>
   </Style.Triggers>
</Style>

สิ่งที่ฉันไม่ชอบคือการทำซ้ำ TextBlock และการผูกข้อความฉันไม่รู้ว่า II สามารถประกาศสิ่งนั้นได้ในที่เดียว

ฉันหวังว่านี่จะช่วยใครสักคน!


นี่เป็นวิธีแก้ปัญหาที่ยอดเยี่ยมและฉันใช้แบบที่คล้ายกัน แต่คุณต้องการเทมเพลตการควบคุมเพียงรายการเดียว หากผู้ใช้จะดับเบิ้ลคลิก a listviewitemพวกเขาอาจไม่สนใจว่าได้เลือกไว้แล้วหรือไม่ นอกจากนี้สิ่งสำคัญคือต้องสังเกตว่าเอฟเฟกต์ไฮไลต์อาจต้องได้รับการปรับแต่งเพื่อให้เข้ากับlistviewสไตล์ โหวตขึ้น
David Bentley

1

ฉันประสบความสำเร็จในการสร้างฟังก์ชันนี้ด้วย. Net 4.7 framework โดยใช้ไลบรารีการโต้ตอบก่อนอื่นตรวจสอบให้แน่ใจว่าได้ประกาศเนมสเปซในไฟล์ XAML

xmlns: i = "http://schemas.microsoft.com/expression/2010/interactivity"

จากนั้นตั้งค่า Event Trigger ด้วย InvokeCommandAction ตามลำดับภายใน ListView ดังต่อไปนี้

ดู:

<ListView x:Name="lv" IsSynchronizedWithCurrentItem="True" 
          ItemsSource="{Binding Path=AppsSource}"  >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseDoubleClick">
            <i:InvokeCommandAction CommandParameter="{Binding ElementName=lv, Path=SelectedItem}"
                                   Command="{Binding OnOpenLinkCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
            <GridViewColumn Header="Developed By" DisplayMemberBinding="{Binding DevelopedBy}" />
        </GridView>
    </ListView.View>
</ListView>

การปรับโค้ดด้านบนน่าจะเพียงพอที่จะทำให้เหตุการณ์ดับเบิลคลิกทำงานบน ViewModel ของคุณได้อย่างไรก็ตามฉันได้เพิ่มคลาส Model และ View Model ให้คุณจากตัวอย่างของฉันเพื่อให้คุณสามารถมีแนวคิดทั้งหมดได้

รุ่น:

public class ApplicationModel
{
    public string Name { get; set; }

    public string DevelopedBy { get; set; }
}

ดูรุ่น:

public class AppListVM : BaseVM
{
        public AppListVM()
        {
            _onOpenLinkCommand = new DelegateCommand(OnOpenLink);
            _appsSource = new ObservableCollection<ApplicationModel>();
            _appsSource.Add(new ApplicationModel("TEST", "Luis"));
            _appsSource.Add(new ApplicationModel("PROD", "Laurent"));
        }

        private ObservableCollection<ApplicationModel> _appsSource = null;

        public ObservableCollection<ApplicationModel> AppsSource
        {
            get => _appsSource;
            set => SetProperty(ref _appsSource, value, nameof(AppsSource));
        }

        private readonly DelegateCommand _onOpenLinkCommand = null;

        public ICommand OnOpenLinkCommand => _onOpenLinkCommand;

        private void OnOpenLink(object commandParameter)
        {
            ApplicationModel app = commandParameter as ApplicationModel;

            if (app != null)
            {
                //Your code here
            }
        }
}

ในกรณีที่คุณต้องการใช้คลาสDelegateCommand


0

นี่คือพฤติกรรมที่ทำได้ทั้งในListBoxและListView.

public class ItemDoubleClickBehavior : Behavior<ListBox>
{
    #region Properties
    MouseButtonEventHandler Handler;
    #endregion

    #region Methods

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.PreviewMouseDoubleClick += Handler = (s, e) =>
        {
            e.Handled = true;
            if (!(e.OriginalSource is DependencyObject source)) return;

            ListBoxItem sourceItem = source is ListBoxItem ? (ListBoxItem)source : 
                source.FindParent<ListBoxItem>();

            if (sourceItem == null) return;

            foreach (var binding in AssociatedObject.InputBindings.OfType<MouseBinding>())
            {
                if (binding.MouseAction != MouseAction.LeftDoubleClick) continue;

                ICommand command = binding.Command;
                object parameter = binding.CommandParameter;

                if (command.CanExecute(parameter))
                    command.Execute(parameter);
            }
        };
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.PreviewMouseDoubleClick -= Handler;
    }

    #endregion
}

นี่คือคลาสส่วนขยายที่ใช้ค้นหาพาเรนต์

public static class UIHelper
{
    public static T FindParent<T>(this DependencyObject child, bool debug = false) where T : DependencyObject
    {
        DependencyObject parentObject = VisualTreeHelper.GetParent(child);

        //we've reached the end of the tree
        if (parentObject == null) return null;

        //check if the parent matches the type we're looking for
        if (parentObject is T parent)
            return parent;
        else
            return FindParent<T>(parentObject);
    }
}

การใช้งาน:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:coreBehaviors="{{Your Behavior Namespace}}"


<ListView AllowDrop="True" ItemsSource="{Binding Data}">
    <i:Interaction.Behaviors>
       <coreBehaviors:ItemDoubleClickBehavior/>
    </i:Interaction.Behaviors>

    <ListBox.InputBindings>
       <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding YourCommand}"/>
    </ListBox.InputBindings>
</ListView>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.