2011-02-04 12 views
40

Zajmuję się refaktoryzacją prostej aplikacji do śledzenia MVVM i moje pytanie brzmi: jak przenieść zdarzenie SelectionChanged z mojego kodu do widokuModel? Przyjrzałem się niektórym przykładom elementów wiążących komend, ale nie byłem w stanie ich zrozumieć. Czy ktoś może w tym pomóc? Dzięki!Zdarzenia związane z UI wiązania do poleceń w ViewModel

Czy ktoś może dostarczyć rozwiązanie za pomocą poniższego kodu? Wielkie dzięki!

public partial class MyAppView : Window 
{ 
    public MyAppView() 
    { 
     InitializeComponent(); 

     this.DataContext = new MyAppViewModel(); 

     // Insert code required on object creation below this point. 
    } 

    private void contactsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) 
    { 
     //TODO: Add event handler implementation here.   
     //for each selected contact get the labels and put in collection 

     ObservableCollection<AggregatedLabelModel> contactListLabels = new ObservableCollection<AggregatedLabelModel>(); 

     foreach (ContactListModel contactList in contactsList.SelectedItems) 
     { 
      foreach (AggregatedLabelModel aggLabel in contactList.AggLabels) 
      { 
       contactListLabels.Add(aggLabel); 
      } 
     } 
     //aggregate the contactListLabels by name 
     ListCollectionView selectedLabelsView = new ListCollectionView(contactListLabels); 

     selectedLabelsView.GroupDescriptions.Add(new PropertyGroupDescription("Name")); 
     tagsList.ItemsSource = selectedLabelsView.Groups; 
    } 
} 

Odpowiedz

83

należy użyć EventTrigger w połączeniu z InvokeCommandAction od nazw Windows.Interactivity. Oto przykład:

<ListBox ...> 
    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="SelectionChanged"> 
      <i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/> 
     </i:EventTrigger> 
    </i:Interaction.Triggers> 
</ListBox> 

Można odwołać System.Windows.Interactivity przechodząc Add reference > Assemblies > Extensions.

Pełna przestrzeń nazw i to: xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity".

+0

Dzięki. Jestem początkującym programistą, więc wybacz mi - czy jesteś w stanie podać przykład używając kodu, który podałem? – Ben

+1

Po prostu trzeba utworzyć właściwość polecenia w swoim ViewModel o nazwie "SelectedItemChangedCommand". Rozkazy są podobne do zdarzeń, ale polecenie może mieć tylko jedną funkcję zwrotną, w przeciwieństwie do zdarzeń. Sprawdź dokument: http://msdn.microsoft.com/en-us/library/ms752308.aspx – Brian

+1

Jeśli nie masz Expression Blend, będziesz potrzebował SDK: http://www.microsoft.com/downloads/ pl/details.aspx? FamilyID = D197F51A-DE07-4EDF-9CBA-1F1B4A22110D i displaylang = en – Murven

8

To pytanie ma podobny problem.

WPF MVVM : Commands are easy. How to Connect View and ViewModel with RoutedEvent

Tak jak ja sobie z tym problemem jest mieć właściwość SelectedItem w ViewModel, a następnie powiązać SelectedItem Twojego ListBox lub cokolwiek do tej nieruchomości.

+1

poprawny punkt, ale nie dotyczy wszystkich scenariuszy ... Mam scenariusz, w którym robię to, co sugerujesz, ale także musisz użyć zdarzenia SelectionChanged – Jordan

0

Jak wspomina @Cameron MacFarland, po prostu dwukierunkowe powiązanie z właściwością modelu viewModel. W ustawieniach nieruchomości możesz wykonać dowolną logikę, na przykład dodać do listy kontaktów, w zależności od wymagań.

Jednak nie musiałbym wywoływać właściwości "SelectedItem", ponieważ viewModel nie powinien wiedzieć o warstwie widoku i jego interakcji z jego właściwościami. Nazwałbym to czymś takim jak CurrentContact czy coś takiego.

Oczywiście jest to chyba po prostu chcesz utworzyć polecenia jako ćwiczenie ćwiczyć itp

+1

Nie zgadzam się że model widoku nie powinien wiedzieć o warstwie widoku. Jest to przecież model widzenia. Nie powinien manipulować obiektami w widoku, ale tylko dlatego, że może być instancjonowany niezależnie od widoku w testach jednostkowych. Nie wywołuję właściwości 'SelectedItem', chyba że kolekcja modelu widoku została nazwana' Items', ale to inny problem. –

+0

Zobacz, co masz na myśli, ale raczej nie postrzegam vm jako "modelu widoku", bardziej jako adapter, który nie przyjmuje żadnych założeń dotyczących interfejsu użytkownika, ale po prostu ujawnia jego stan i zachowanie za pomocą poleceń i powiadomień. Ta seperacja nie jest "tylko" dla testów jednostkowych, jak mówisz, ale oznacza także, że interfejs użytkownika może być łatwo przełączany i wyłączany oraz modyfikowany w razie potrzeby bez konieczności modyfikowania maszyny wirtualnej. – HAdes

7

Do tego trzeba zmieniać swoje myślenie byłaby. Nie będziesz już obsługiwał zdarzenia "zmienionego wyboru", ale raczej zapisuje wybrany element w swoim widoku. Następnie używałbyś dwukierunkowego powiązania danych, tak aby po wybraniu elementu twój model wyświetlania został zaktualizowany, a po zmianie wybranego elementu jego widok został zaktualizowany.

+0

Myślę, że część myślenia shifing jest tam, gdzie walczę jako dość początkujący programista! – Ben

1

chciałbym podążać górną odpowiedź w tym question

Zasadniczo Państwa zdanie modelu będzie zawierać listę wszystkich przedmiotów oraz listę wybranych przedmiotów. Następnie możesz dołączyć zachowanie do listy, która zarządza twoją listą wybranych elementów.

Dzięki temu nie masz nic w kodzie, a Xaml jest dość łatwy do naśladowania, a zachowanie może być ponownie użyte w innym miejscu w aplikacji.

<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" /> 
2
<ListBox SelectionChanged="{eb:EventBinding Command=SelectedItemChangedCommand, CommandParameter=$e}"> 

</ListBox> 

poleceń

{eb: EventBinding} (Simple nazewnictwa wzorzec znaleźć poleceń)

{eb: EventBinding polecenia = nazwa_polecenia}

CommandParameter

$ e (EventAgrs)

$ to albo $ this.Property

ciąg

https://github.com/JonghoL/EventBindingMarkup

1

Czasami rozwiązanie wiążących zdarzenie dowodzić poprzez Interaktywność wyzwalacz nie działa, gdy jest to potrzebne wiązać zdarzenie niestandardowej kontroli użytkownika. W tym przypadku można użyć niestandardowego zachowania. zachowanie wiązania

Declare jak:

public class PageChangedBehavior 
{ 
    #region Attached property 

    public static ICommand PageChangedCommand(DependencyObject obj) 
    { 
     return (ICommand)obj.GetValue(PageChangedCommandProperty); 
    } 
    public static void SetPageChangedCommand(DependencyObject obj, ICommand value) 
    { 
     obj.SetValue(PageChangedCommandProperty, value); 
    } 

    public static readonly DependencyProperty PageChangedCommandProperty = 
     DependencyProperty.RegisterAttached("PageChangedCommand", typeof(ICommand), typeof(PageChangedBehavior), 
      new PropertyMetadata(null, OnPageChanged)); 

    #endregion 

    #region Attached property handler 

    private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var control = d as PageControl; 
     if (control != null) 
     { 
      if (e.NewValue != null) 
      { 
       control.PageChanged += PageControl_PageChanged; 
      } 
      else 
      { 
       control.PageChanged -= PageControl_PageChanged; 
      } 
     } 
    } 

    static void PageControl_PageChanged(object sender, int page) 
    { 
     ICommand command = PageChangedCommand(sender as DependencyObject); 

     if (command != null) 
     { 
      command.Execute(page); 
     } 
    } 

    #endregion 

}

A następnie powiązać go z komendy w XAML:

 <controls:PageControl 
      Grid.Row="2" 
      CurrentPage="{Binding Path=UsersSearchModel.Page,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
      PerPage="{Binding Path=UsersSearchModel.PageSize,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
      Count="{Binding Path=UsersSearchModel.SearchResults.TotalItemCount}" 
      behaviors:PageChangedBehavior.PageChangedCommand="{Binding PageChangedCommand}"> 
     </controls:PageControl> 
Powiązane problemy