2010-12-21 6 views
25

(! Here क्षमा करें नोट इस एक फिर से पोस्ट के रूप में मेरा पहला सवाल गलत शीर्षक के अंतर्गत पोस्ट किया गया है)।WPF/MVVM - ViewModel में TreeViewItems पर डबल-क्लिक कैसे करें?</p> <p>मैं एक मानक WPF treeview है और मॉडल कक्षाओं देखने पर आइटम के लिए बाध्य किया है: -

आइटम अब डबल-क्लिक किए जाने पर व्यवहार को संभालना चाहते हैं (दस्तावेज दृश्य-स्टूडियो-शैली खोलना)।

मैं घटना-हैंडलर को ट्रीव्यूव (xaml दिखाया गया) के नियंत्रण में आग लग सकता हूं, लेकिन मैं दृश्य मॉडल कक्षाओं पर विशिष्ट व्यवहार से कैसे जुड़ूं - उदा। ProjectViewModel?

बेहतर, ICommand-implementer के लिए बाध्य कर के रूप में इस कहीं और प्रयोग किया जाता है ...

<TreeView ItemsSource="{Binding Projects}" MouseDoubleClick="TreeView_MouseDoubleClick"> 
    <TreeView.ItemContainerStyle> 
     <!-- 
This Style binds a TreeViewItem to a TreeViewItemViewModel. 
--> 
     <Style TargetType="{x:Type TreeViewItem}"> 
      <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> 
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> 
      <Setter Property="FontWeight" Value="Normal" /> 
      <Style.Triggers> 
       <Trigger Property="IsSelected" Value="True"> 
        <Setter Property="FontWeight" Value="Bold" /> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
    </TreeView.ItemContainerStyle> 

    <TreeView.Resources> 
     <HierarchicalDataTemplate DataType="{x:Type Implementations:ProjectViewModel}" ItemsSource="{Binding Children}"> 
      <StackPanel Orientation="Horizontal"> 
       <Image Width="16" Height="16" Margin="3,0" Source="Images\Region.png" /> 
       <TextBlock Text="{Binding DisplayName}" /> 
      </StackPanel> 
     </HierarchicalDataTemplate> 

     <HierarchicalDataTemplate DataType="{x:Type Implementations:PumpViewModel}" ItemsSource="{Binding Children}"> 
      <StackPanel Orientation="Horizontal"> 
       <Image Width="16" Height="16" Margin="3,0" Source="Images\State.png" /> 
       <TextBlock Text="{Binding Name}" /> 
      </StackPanel> 
     </HierarchicalDataTemplate> 

     <DataTemplate DataType="{x:Type Implementations:PumpDesignViewModel}"> 
      <StackPanel Orientation="Horizontal"> 
       <Image Width="16" Height="16" Margin="3,0" Source="Images\City.png" /> 
       <TextBlock Text="{Binding Name}" /> 
      </StackPanel> 
     </DataTemplate> 
    </TreeView.Resources> 
</TreeView> 
+0

मैं प्रदर्शित करने के लिए संबंध है, आलसी लोड आदि हालांकि, के साथ व्यक्तिगत treeitems निपटने viewmodel वस्तुओं है जब मैं 'सक्रिय'/डबल क्लिक करें treeview में एक आइटम, मैं चाहता हूँ करना चाहते हैं यह क्रिया उसी दृश्यमान वस्तुओं से संभाली जा सकती है जो प्रदर्शन को संभालती है - लेकिन मैं यह कैसे कर सकता हूं? –

+0

क्या आपने संलग्न व्यवहार को देखने की कोशिश की है? –

+0

ListView का उपयोग कर समान प्रश्न, लेकिन उत्तर नियंत्रण-अज्ञेयवादी हैं http://stackoverflow.com/questions/1035023/firing-a-double-click-event-from-a-wpf-listview-item-using-mvvm/1510592# 1510592 – surfen

उत्तर

49

मेरा उत्तर एक सा अपडेट कर रहा है।

मैंने इसके लिए अलग-अलग दृष्टिकोणों की कोशिश की है और मुझे अभी भी संलग्न व्यवहारों का अनुभव है सबसे अच्छा समाधान है। यद्यपि शुरुआत में यह ओवरहेड की तरह दिख सकता है, यह वास्तव में नहीं है। मैं अपने सभी व्यवहारों को ICommands के लिए एक ही स्थान पर रखता हूं और जब भी मुझे किसी अन्य ईवेंट के लिए समर्थन की आवश्यकता होती है तो यह सिर्फ कॉपी/पेस्ट की बात है और PropertyChangedCallback में ईवेंट को बदलता है।

मैंने CommandParameter के लिए वैकल्पिक समर्थन भी जोड़ा।

डिजाइनर में यह वांछित घटना

enter image description here

का चयन का मामला आप इस सेट कर सकते हैं या तो TreeView, TreeViewItem या किसी अन्य जगह है कि आप की तरह पर।

उदाहरण। इसे TreeView

<TreeView commandBehaviors:MouseDoubleClick.Command="{Binding YourCommand}" 
      commandBehaviors:MouseDoubleClick.CommandParameter="{Binding}" 
      .../> 

उदाहरण पर सेट करें। TreeViewItem

<TreeView ItemsSource="{Binding Projects}"> 
    <TreeView.ItemContainerStyle> 
     <Style TargetType="{x:Type TreeViewItem}"> 
      <Setter Property="commandBehaviors:MouseDoubleClick.Command" 
        Value="{Binding YourCommand}"/> 
      <Setter Property="commandBehaviors:MouseDoubleClick.CommandParameter" 
        Value="{Binding}"/> 
     </Style> 
    </TreeView.ItemContainerStyle> 
</TreeView> 

पर यह सेट और यहाँ संलग्न व्यवहारMouseDoubleClick

public class MouseDoubleClick 
{ 
    public static DependencyProperty CommandProperty = 
     DependencyProperty.RegisterAttached("Command", 
     typeof(ICommand), 
     typeof(MouseDoubleClick), 
     new UIPropertyMetadata(CommandChanged)); 

    public static DependencyProperty CommandParameterProperty = 
     DependencyProperty.RegisterAttached("CommandParameter", 
              typeof(object), 
              typeof(MouseDoubleClick), 
              new UIPropertyMetadata(null)); 

    public static void SetCommand(DependencyObject target, ICommand value) 
    { 
     target.SetValue(CommandProperty, value); 
    } 

    public static void SetCommandParameter(DependencyObject target, object value) 
    { 
     target.SetValue(CommandParameterProperty, value); 
    } 
    public static object GetCommandParameter(DependencyObject target) 
    { 
     return target.GetValue(CommandParameterProperty); 
    } 

    private static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) 
    { 
     Control control = target as Control; 
     if (control != null) 
     { 
      if ((e.NewValue != null) && (e.OldValue == null)) 
      { 
       control.MouseDoubleClick += OnMouseDoubleClick; 
      } 
      else if ((e.NewValue == null) && (e.OldValue != null)) 
      { 
       control.MouseDoubleClick -= OnMouseDoubleClick; 
      } 
     } 
    } 

    private static void OnMouseDoubleClick(object sender, RoutedEventArgs e) 
    { 
     Control control = sender as Control; 
     ICommand command = (ICommand)control.GetValue(CommandProperty); 
     object commandParameter = control.GetValue(CommandParameterProperty); 
     command.Execute(commandParameter); 
    } 
} 
+0

@ एंडर्स जुल: क्या आपको आउटपुट विंडो में कोई बाध्यकारी त्रुटियां मिलती हैं? यह प्रश्न देखें: http://stackoverflow.com/questions/4051713/what-are-the-tools-available-for-troubleshooting-wpf-and-silverlight-databinding/4051953#4051953 सभी को डेटा बाध्यकारी आउटपुट कैसे सेट करें यदि आपके पास यह –

+0

चालू नहीं है, तो कोई भी आइटम एक्टिवेटेड नामक एक संलग्न ईवेंट भी बना सकता है, जो एंटर कुंजी को भी संभालता है: http://serialseb.blogspot.com/2007/01/attached-events-by-example- adding.html। आप उस घटना को उसी उत्तर में सुझाएंगे जैसा कि इस उत्तर में सुझाया गया है। – surfen

+0

मैंने ट्री व्यू कंटेनर शैली के लिए अपना दृष्टिकोण उपयोग किया है, यह ऐप में ठीक काम करता है, लेकिन डिज़ाइनर एक त्रुटि दिखाता है: "संपत्ति" कमांड "निर्भरता प्रॉपर्टी नहीं है। मार्कअप में उपयोग करने के लिए, गैर-संलग्न गुणों को अवगत कराया जाना चाहिए एक सुलभ उदाहरण संपत्ति "कमांड" के साथ लक्ष्य प्रकार। संलग्न गुणों के लिए, घोषित प्रकार को स्थिर "GetCommand" और "SetCommand" विधियां प्रदान करनी होंगी। " लेकिन आपकी कक्षा में SetCommand और GetCommand विधियां हैं, इसलिए मुझे नहीं पता कि यह त्रुटि क्यों है। इसे कैसे जोड़ेंगे? इस त्रुटि को ठीक किए बिना, मेरी सभी एक्सएएमएल शैलियों को पूरे समाधान में लोड नहीं किया गया है। –

9

मैं इस के लिए देर से कर रहा हूँ, लेकिन मैं सिर्फ एक अलग समाधान का इस्तेमाल किया। एक बार फिर, यह सबसे अच्छा नहीं हो सकता है, लेकिन यहां मैंने ऐसा किया है।

सबसे पहले, मेलेक का पिछला जवाब अच्छा है, लेकिन मुझे लगता है कि माउसबब्लिकक्लिक के रूप में मूलभूत चीज़ों के लिए अटैचमेंटबेविर्स को जोड़ने के लिए मजबूर होना बहुत भारी है। यह मुझे मेरे ऐप में एक नए पैटर्न का उपयोग करने के लिए मजबूर करेगा और इससे भी सबकुछ जटिल होगा।

मेरा लक्ष्य जितना संभव हो उतना सरल रहना है।इसलिए मैं बहुत ही बुनियादी कुछ किया (मेरे उदाहरण एक डेटा ग्रिड के लिए है, लेकिन आप का उपयोग कर सकते विभिन्न नियंत्रणों का एक बहुत पर):

<DataGrid MouseDoubleClick="DataGrid_MouseDoubleClick"> 
    <!-- ... --> 
</DataGrid> 

कोड-पीछे में:

private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) 
{ 
    //Execute the command related to the doubleclick, in my case Edit 
    (this.DataContext as VmHome).EditAppCommand.Execute(null); 
} 

मैं क्यों ऐसा लगता है कि यह एमवीवीएम-पैटर्न को तोड़ता नहीं है? क्योंकि मेरी राय में, कोड-बैक में आपको केवल एक ही चीज डालना चाहिए, जो आपके दृश्य में पुल हैं, मॉडल, आपके यूआई के लिए बहुत विशिष्ट चीजें हैं। इस मामले में यह सिर्फ इतना कहता है कि यदि आप डबल क्लिक करते हैं, तो संबंधित कमांड को आग लगाएं। यह लगभग कमांड = "{बाध्यकारी EditAppCommand}" जैसा ही है, मैंने अभी इस व्यवहार को अनुकरण किया है।

मुझे इस पर आपकी राय देने के लिए स्वतंत्र महसूस करें, मुझे कुछ आलोचकों को इस तरह सोचने में खुशी होगी, लेकिन अब मुझे विश्वास है कि यह एमवीवीएम तोड़ने के बिना इसे लागू करने का सबसे आसान तरीका है।

+1

@ डेमस्कस: वैकल्पिक समाधान के लिए धन्यवाद। मेरी राय में, टीटी वास्तव में इस बात पर निर्भर करता है कि आपको कितनी बार ऐसा करने की आवश्यकता होगी। ध्यान दें कि एक संलग्न Behaviour समाधान सामान्य है - नियंत्रण अपरिवर्तनीय है, इसलिए यह पुन: प्रयोज्यता और रखरखाव को बढ़ावा देता है, क्योंकि आपके पास एक्सएएमएल में घोषित सभी आपके आदेश होंगे। सभी डब्ल्यूपीएफ के साथ प्रस्तावों को मैप करने के लिए कोडबेइंड लिखना है, मुझे लगता है कि मेरे लिए बहुत ज्यादा उपर है। मेरी इच्छा है कि सभी घटनाओं ने बॉक्स के बाहर कमांड बाध्यकारी का समर्थन किया। – surfen

+0

ट्री व्यू के मामले में, आप वास्तव में TreeViewItem के DataContext पर रूचि रखते हैं जिस पर डबल क्लिक किया गया है, लेकिन आपका समाधान ViewModel को ऐसी जानकारी नहीं दे रहा है। – JoanComasFdz

0

मेरे द्वारा पहुंचा जाने वाला सबसे अच्छा तरीका सिर्फ IsSelected संपत्ति को TreeViewItem से व्यूमोडेल में दो-तरफा मोड में बाध्यकारी है और संपत्ति सेटटर में तर्क लागू करें। फिर आप परिभाषित कर सकते हैं कि क्या मूल्य सही है या गलत है, क्योंकि जब भी कोई उपयोगकर्ता किसी आइटम पर क्लिक करता है तो यह संपत्ति बदलेगी।

class MyVM 
{ 
    private bool _isSelected; 
    public bool IsSelected 
    { 
    get { return _isSelected; } 
    set 
    { 
     if (_isSelected == null) 
     return; 

     _isSelected = vale; 

     if (_isSelected) 
     { 
     // Your logic goes here. 
     } 
     else 
     { 
     // Your other logic goes here. 
     } 
    } 
} 

यह बहुत सारे कोड से बचाता है।

इसके अलावा, यह तकनीक आपको केवल व्यूमोडल्स में "ऑनक्लिक" व्यवहार को लागू करने की अनुमति देती है, जिसे वास्तव में इसकी आवश्यकता होती है।

+1

लेकिन आप सवाल को याद करते हैं, क्योंकि ओपी "डबल क्लिक" ईवेंट को संभालना चाहता है। आपका कोड बहुत अच्छा है, लेकिन केवल "क्लिक" ईवेंट के लिए काम करता है। –

+0

@ जुलिएन हां, आप सही हैं। – JoanComasFdz

2

Meleak समाधान महान है !, लेकिन मैं जाँच जोड़ा

private static void OnMouseDoubleClick(object sender, RoutedEventArgs e) 
    { 
     Control control = sender as Control; 
     ICommand command = (ICommand)control.GetValue(CommandProperty); 
     object commandParameter = control.GetValue(CommandParameterProperty); 
     //Check command can execute!! 
     if(command.CanExecute(commandParameter)) 
     command.Execute(commandParameter); 
    } 
5

दोनों Meleak और इगोर की सिफारिशों महान हैं, लेकिन जब डबल क्लिक ईवेंट हैंडलर तो TreeViewItemके लिए बाध्य है इस ईवेंट हैंडलर कहा जाता है सभी आइटम के मूल तत्वों के लिए (केवल क्लिक किए गए तत्व नहीं)। यदि यह वांछित नहीं है, यहाँ एक और है:

private static void OnMouseDoubleClick(object sender, RoutedEventArgs e) 
{ 
    Control control = sender as Control; 
    ICommand command = (ICommand)control.GetValue(CommandProperty); 
    object commandParameter = control.GetValue(CommandParameterProperty); 

    if (sender is TreeViewItem) 
    { 
     if (!((TreeViewItem)sender).IsSelected) 
      return; 
    } 

    if (command.CanExecute(commandParameter)) 
    { 
     command.Execute(commandParameter); 
    } 
} 
+0

यह वास्तव में मेरे लिए बहुत उपयोगी था। एकमात्र मामूली समस्या यह है कि यह माना जाता है कि 'प्रेषक' वास्तव में चुना जाता है, जो हमेशा डबल क्लिक के लिए सच होता है, लेकिन जब आप घटना प्रकार को किसी अन्य चीज़ में नहीं बदलते हैं (यानी 'माउसहोवर' या ऐसी चीजें)। – Yellow

0
बस जिज्ञासा के लिए

: क्या हुआ अगर मैं Frederiks हिस्सा लेते हैं, लेकिन व्यवहार के रूप में सीधे इसे लागू?

public class MouseDoubleClickBehavior : Behavior<Control> 
{ 
    public static readonly DependencyProperty CommandProperty = 
     DependencyProperty.Register("Command", typeof (ICommand), typeof (MouseDoubleClickBehavior), new PropertyMetadata(default(ICommand))); 

    public ICommand Command 
    { 
     get { return (ICommand) GetValue(CommandProperty); } 
     set { SetValue(CommandProperty, value); } 
    } 

    public static readonly DependencyProperty CommandParameterProperty = 
     DependencyProperty.Register("CommandParameter", typeof (object), typeof (MouseDoubleClickBehavior), new PropertyMetadata(default(object))); 

    public object CommandParameter 
    { 
     get { return GetValue(CommandParameterProperty); } 
     set { SetValue(CommandParameterProperty, value); } 
    } 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 
     AssociatedObject.MouseDoubleClick += OnMouseDoubleClick; 
    } 

    protected override void OnDetaching() 
    { 
     AssociatedObject.MouseDoubleClick -= OnMouseDoubleClick; 
     base.OnDetaching(); 
    } 

    void OnMouseDoubleClick(object sender, RoutedEventArgs e) 
    { 
     if (Command == null) return; 
     Command.Execute(/*commandParameter*/null); 
    } 
} 
2

यह वास्तव में आसान है और यह कैसे मैं TreeView पर डबल क्लिक करें संभाला है:

<Window x:Class="TreeViewWpfApplication.MainWindow" 
    ... 
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
    ...> 

     <TreeView ItemsSource="{Binding Departments}" > 
     <i:Interaction.Triggers> 
      <i:EventTrigger EventName="MouseDoubleClick"> 
       <ei:CallMethodAction MethodName="SomeMethod" TargetObject="{Binding}"/> 
      </i:EventTrigger> 
     </i:Interaction.Triggers> 
     </TreeView> 
</Window> 

System.Windows.Interactivity.dll सी से लिया जाता है: \ Program Files (x86) \ Microsoft SDKs \ अभिव्यक्ति \ Blend.NETFramework \ v4.0 \ पुस्तकालय \ System.Windows.Interactivity.dll या द्वारा NuGet

मेरा विचार मॉडल:

+०१२३५१६४१०६१
public class TreeViewModel : INotifyPropertyChanged 
{ 
    private List<Department> departments; 
    public TreeViewModel() 
    { 
     Departments = new List<Department>() 
     { 
      new Department("Department1"), 
      new Department("Department2"), 
      new Department("Department3") 
     }; 
    } 

    public List<Department> Departments 
    { 
     get 
     { 
      return departments; 
     } 
     set 
     { 
      departments = value; 
      OnPropertyChanged("Departments"); 
     } 
    } 

    public void SomeMethod() 
    { 
     MessageBox.Show("*****"); 
    } 
} 
+0

+1। यह मेरे लिए काम करता है। मैं हमेशा इंटरएक्टिविटी-आधारित दृष्टिकोण पसंद करता हूं। और यह आपके काम के रूप में काम करता है - NuGet के माध्यम से पैकेज स्थापित करें, I: Interaction.Triggers अनुभाग बिट डालें, ViewModel क्लास में परीक्षण विधि जोड़ें और आप दूर हैं ... – AndyUK

0

माउस TextBlock पर बाध्यकारी

देखें की TreeView.Resources में:

<HierarchicalDataTemplate 
     DataType="{x:Type treeview:DiscoveryUrlViewModel}" 
     ItemsSource="{Binding Children}"> 

     <StackPanel Orientation="Horizontal"> 
      <Image Width="16" Height="16" Margin="3,0" Source="../Images/ic_search.png" /> 

      <TextBlock Text="{Binding DisplayText}" > 
       <TextBlock.InputBindings> 
        <MouseBinding Gesture="LeftDoubleClick" 
            Command="{Binding DoubleClickCopyCommand}" 
            CommandParameter="{Binding }" /> 
       </TextBlock.InputBindings> 
      </TextBlock> 
     </StackPanel> 
</HierarchicalDataTemplate> 

कि देखें की ViewModel (DiscoveryUrlViewModel में।सीएस):

private RelayCommand _doubleClickCommand; 
public ICommand DoubleClickCopyCommand 
     { 
      get 
      { 
       if (_doubleClickCommand == null) 
        _doubleClickCommand = new RelayCommand(OnDoubleClick); 
       return _doubleClickCommand; 
      } 
     } 

     private void OnDoubleClick(object obj) 
     { 
      var clickedViewModel = (DiscoveryUrlViewModel)obj; 
     } 
संबंधित मुद्दे