Ich arbeite gerade an einer einfachen Anwendung, um MVVM zu folgen, und meine Frage ist, wie ich ein SelectionChanged-Ereignis aus meinem Code in das viewModel verschieben kann. Ich habe mir einige Beispiele von Bindungselementen an Befehlen angesehen, habe es aber nicht ganz verstanden. Kann jemand mithelfen. Vielen Dank!
Kann jemand eine Lösung bereitstellen, die den folgenden Code verwendet? Danke vielmals!
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;
}
}
Sie sollten eine EventTrigger
zusammen mit InvokeCommandAction
aus dem Windows.Interactivity-Namespace verwenden. Hier ist ein Beispiel:
<ListBox ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
Sie können auf System.Windows.Interactivity
verweisen, indem Sie Add reference > Assemblies > Extensions
aufrufen.
Der vollständige i
-Namespace lautet: xmlns:i="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"
.
Diese Frage hat ein ähnliches Problem.
WPF MVVM: Befehle sind einfach. So verbinden Sie View und ViewModel mit RoutedEvent
Ich gehe mit diesem Problem um, wenn ich eine SelectedItem-Eigenschaft im ViewModel habe und dann das SelectedItem Ihrer ListBox oder was auch immer an diese Eigenschaft binde.
Ihre beste Wette ist Windows.Interactivity
. Verwenden Sie EventTriggers
, um eine ICommand
an eine RoutedEvent
anzuhängen.
Hier ist ein Artikel, mit dem Sie beginnen können: Silverlight und WPF-Verhalten und -Auslöser
Um dies zu ändern, müssen Sie Ihr Denken ändern. Sie behandeln kein Ereignis "Auswahl geändert" mehr, sondern speichern das ausgewählte Element in Ihrem Ansichtsmodell. Sie würden dann die bidirektionale Datenbindung verwenden, damit das Ansichtsmodell aktualisiert wird, wenn der Benutzer ein Element auswählt. Wenn Sie das ausgewählte Element ändern, wird Ihre Ansicht aktualisiert.
<ListBox SelectionChanged="{eb:EventBinding Command=SelectedItemChangedCommand, CommandParameter=$e}">
</ListBox>
Befehl
{eb: EventBinding} (Ein einfaches Benennungsmuster, um den Befehl zu finden)
{eb: EventBinding-Befehl = Befehlsname}
Befehlsparameter
$ e (EventAgrs)
$ this oder $ this.Property
schnur
Ich würde der Top-Antwort in dieser Frage folgen
Grundsätzlich enthält Ihr Ansichtsmodell eine Liste aller Elemente und eine Liste der ausgewählten Elemente. Sie können dann an Ihre Listbox ein Verhalten anhängen, das Ihre Liste der ausgewählten Elemente verwaltet.
Wenn Sie dies tun, bedeutet dies, dass Sie nichts im Code hinter sich haben, und das XAML ist ziemlich einfach zu befolgen. Außerdem kann das Verhalten an anderer Stelle in Ihrer App verwendet werden.
<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" />
Manchmal funktioniert die Lösung des Bindungsereignisses für den Befehl über den Interaktivitätsauslöser nicht, wenn das Ereignis der benutzerdefinierten Benutzerkontrolle ..__ gebunden werden muss. In diesem Fall können Sie benutzerdefiniertes Verhalten verwenden.
Bindungsverhalten wie folgt deklarieren:
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
}
Und dann binden Sie es an den Befehl in 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>
Betrachten Sie Microsoft.Xaml.Behaviors.Wpf , dessen Besitzer Microsoft
ist, die Sie auf dieser Seite sehen können.
System.Windows.Interactivity.WPF Besitzer ist mthamil
, kann mir jeder sagen, ob es zuverlässig ist?
Beispiel für Microsoft.Xaml.Behaviors.Wpf
:
<UserControl ...
xmlns:behaviors="http://schemas.Microsoft.com/xaml/behaviors"
...>
<Button x:Name="button">
<behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="Click" SourceObject="{Binding ElementName=button}">
<behaviors:InvokeCommandAction Command="{Binding ClickCommand}" />
</behaviors:EventTrigger>
</behaviors:Interaction.Triggers>
</Button>
</UserControl>
Wie @Cameron MacFarland erwähnt, würde ich einfach eine bidirektionale Bindung an eine Eigenschaft auf dem viewModel vornehmen. Im Eigenschaftssetter können Sie jede gewünschte Logik ausführen, z. B. je nach Ihren Anforderungen zu einer Kontaktliste hinzufügen.
Ich würde die Eigenschaft jedoch nicht unbedingt 'SelectedItem' nennen, da das viewModel die Ansichtsebene und die Interaktion mit ihren Eigenschaften nicht kennen sollte. Ich würde es so etwas wie CurrentContact oder so nennen.
Offensichtlich ist dies der Fall, es sei denn, Sie möchten nur Befehle als Übungsaufgabe usw. erstellen.
Dies ist eine Implementierung mit einem MarkupExtension
. Trotz der niedrigen Ebene (die in diesem Szenario erforderlich ist) ist der XAML-Code sehr einfach:
XAML
<SomeControl Click="{local:EventBinding EventToCommand}" CommandParameter="{local:Int32 12345}" />
Marup Extension
public class EventBindingExtension : MarkupExtension
{
private static readonly MethodInfo EventHandlerImplMethod = typeof(EventBindingExtension).GetMethod(nameof(EventHandlerImpl), new[] { typeof(object), typeof(string) });
public string Command { get; set; }
public EventBindingExtension()
{
}
public EventBindingExtension(string command) : this()
{
Command = command;
}
// Do not use!!
public static void EventHandlerImpl(object sender, string commandName)
{
if (sender is FrameworkElement frameworkElement)
{
object dataContext = frameworkElement.DataContext;
if (dataContext?.GetType().GetProperty(commandName)?.GetValue(dataContext) is ICommand command)
{
object commandParameter = (frameworkElement as ICommandSource)?.CommandParameter;
if (command.CanExecute(commandParameter)) command.Execute(commandParameter);
}
}
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget targetProvider &&
targetProvider.TargetObject is FrameworkElement targetObject &&
targetProvider.TargetProperty is MemberInfo memberInfo)
{
Type eventHandlerType;
if (memberInfo is EventInfo eventInfo) eventHandlerType = eventInfo.EventHandlerType;
else if (memberInfo is MethodInfo methodInfo) eventHandlerType = methodInfo.GetParameters()[1].ParameterType;
else return null;
MethodInfo handler = eventHandlerType.GetMethod("Invoke");
DynamicMethod method = new DynamicMethod("", handler.ReturnType, new[] { typeof(object), typeof(object) });
ILGenerator ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg, 0);
ilGenerator.Emit(OpCodes.Ldstr, Command);
ilGenerator.Emit(OpCodes.Call, EventHandlerImplMethod);
ilGenerator.Emit(OpCodes.Ret);
return method.CreateDelegate(eventHandlerType);
}
else
{
throw new InvalidOperationException("Could not create event binding.");
}
}
}