it-swarm.com.de

WPF: Wie binden Sie einen Befehl mit MVVM an das ListBoxItem?

Ich habe gerade angefangen, MVVM zu lernen. Ich habe die Anwendung von Grund auf erstellt, indem Sie diesem MVVM-Tutorial folgen (ich empfehle es allen MVVM-Einsteigern da draußen). Was ich bisher erstellt habe, sind im Wesentlichen ein paar Textfelder, in denen der Benutzer seine Daten hinzufügt, eine Schaltfläche zum Speichern dieser Daten, die anschließend die ListBox mit allen eingegebenen Einträgen füllt. 

Hier bin ich festgefahren: Ich möchte in der Lage sein, auf ein ListBoxItem doppelt zu klicken und einen Befehl auszulösen, den ich erstellt und meinem ViewModel hinzugefügt habe. Ich kann nicht die XAML-Seite abschließen, d. H. Ich weiß nicht, wie ich diesen Befehl an die ListBox (Item) binden soll. 

Hier ist XAML: 

...
<ListBox 
    Name="EntriesListBox" 
    Width="228" 
    Height="208" 
    Margin="138,12,0,0" 
    HorizontalAlignment="Left" 
    VerticalAlignment="Top" 
    ItemsSource="{Binding Entries}" />
...

Hier ist ViewModel: 

public class MainWindowViewModel : DependencyObject
{
    ...
    public IEntriesProvider Entries
    {
        get { return entries; }
    }

    private IEntriesProvider entries;
    public OpenEntryCommand OpenEntryCmd { get; set; }

    public MainWindowViewModel(IEntriesProvider source)
    {
        this.entries = source;
        ...
        this.OpenEntryCmd = new OpenEntryCommand(this);
    }
    ...
}

Und schließlich ist hier der OpenEntryCommand, den ich ausführen möchte, sobald der Benutzer auf das Element in der EntriesListBox doppelklickt:

public class OpenEntryCommand : ICommand
{
    private MainWindowViewModel viewModel;

    public OpenEntryCommand(MainWindowViewModel viewModel)
    {
        this.viewModel = viewModel;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return parameter is Entry;
    }

    public void Execute(object parameter)
    {
        string messageFormat = "Subject: {0}\nStart: {1}\nEnd: {2}";
        Entry entry = parameter as Entry;
        string message = string.Format(messageFormat, 
                                       entry.Subject, 
                                       entry.StartDate.ToShortDateString(), 
                                       entry.EndDate.ToShortDateString());

        MessageBox.Show(message, "Appointment");
    }
}

Bitte helfen Sie, ich würde es schätzen. 

24
Boris

Leider haben nur ButtonBase-Steuerelemente die Möglichkeit, ICommand-Objekte an ihre Command-Eigenschaften zu binden (für das Click-Ereignis).

Sie können jedoch eine von Blend bereitgestellte API verwenden, um ein Ereignis (wie in Ihrem Fall MouseDoubleClick in der ListBox) einem ICommand-Objekt zuzuordnen. 

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

Sie müssen Folgendes definieren: xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity" und einen Verweis auf System.Windows.Interactivity.dll haben.

- EDIT - .. Dies ist Teil von WPF4, aber Sie können Microsoft.Windows.Interactivity verwenden, wenn Sie nicht WPF4 verwenden. Diese DLL ist von Blend SDK, für das kein Blend erforderlich ist. Von hier aus: http://www.Microsoft.com/downloads/de/details.aspx?FamilyID=f1ae9a30-4928-411d-970b- e682ab179e17 & displaylang = de

Update: Ich habe etwas gefunden, das Ihnen helfen sollte. check dieser Link zu MVVM Light Toolkit , der eine Komplettlösung sowie einen Link enthält zu den benötigten Bibliotheken. MVVM Light Toolkit ist ein sehr interessantes Framework für die Anwendung von MVVM mit Silverlight, WPF und WP7.

Hoffe das hilft :)

61
AbdouMoumen

Dies ist aufgrund des DoubleClick-Ereignisses schwierig. Hierfür gibt es verschiedene Möglichkeiten:

  1. Behandeln Sie das Doppelklick-Ereignis im Code dahinter und rufen Sie dann manuell einen Befehl/eine Methode in Ihrem ViewModel auf
  2. Verwenden Sie ein angefügtes Verhalten, um das DoubleClick-Ereignis an Ihren Befehl weiterzuleiten
  3. Verwenden Sie ein Mischverhalten für ordnen Sie das DoubleClick-Ereignis Ihrem Befehl z

2 und 3 mögen reiner sein, aber ehrlich gesagt ist 1 einfacher, weniger komplex und nicht das Schlimmste auf der Welt. Für einen einmaligen Fall würde ich wahrscheinlich Ansatz Nr. 1 verwenden.

Wenn Sie Ihre Anforderungen geändert haben, um beispielsweise einen Hyperlink für jedes Element zu verwenden, ist dies einfacher. Beginnen Sie mit der Benennung des Stammelements in Ihrer XAML, z. B. für ein Fenster:

<Window .... Name="This">

Verwenden Sie jetzt in der DataTemplate für Ihre ListBox-Elemente Folgendes:

<ListBox ...>
  <ListBox.ItemTemplate>
    <DataTemplate>
      <Hyperlink 
        Command="{Binding ElementName=This, Path=DataContext.OpenEntryCmd}"
        Text="{Binding Path=Name}" 
        />

Mit der ElementName-Bindung können Sie OpenEntryCmd aus dem Kontext Ihres ViewModel auflösen und nicht aus dem spezifischen Datenelement.

7
Paul Stovell

Der beste Weg dazu ist, einen einfachen Wrapper für Benutzersteuerelemente für meinen Inhalt mit Abhängigkeitseigenschaften für Befehl und Parameter zu erstellen.

Der Grund dafür war, dass der Button das Klickereignis nicht zu meiner ListBox sprudelte, was die Auswahl des ListBoxItem verhinderte.

CommandControl.xaml.cs:

public partial class CommandControl : UserControl
{
    public CommandControl()
    {
        MouseLeftButtonDown += OnMouseLeftButtonDown;
        InitializeComponent();
    }

    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
    {
        if (Command != null)
        {
            if (Command.CanExecute(CommandParameter))
            {
                Command.Execute(CommandParameter);
            }
        }
    }

    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand),
            typeof(CommandControl),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));

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

    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object),
            typeof(CommandControl),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));

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

CommandControl.xaml:

<UserControl x:Class="WpfApp.UserControls.CommandControl"
         xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.Microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         Background="Transparent">
</UserControl>

Verwendungszweck:

<ListBoxItem>
    <uc:CommandControl Command="{Binding LoadPageCommand}"
                       CommandParameter="{Binding HomePageViewModel}">
        <TextBlock Text="Home" Margin="0,0,0,5" VerticalAlignment="Center"
                   Foreground="White" FontSize="24" />
    </uc:CommandControl>
</ListBoxItem>

Der Inhalt kann beliebig sein. Wenn Sie auf das Steuerelement klicken, wird der Befehl ausgeführt.

EDIT: Background="Transparent" zu UserControl hinzugefügt, um Klickereignisse im gesamten Bereich des Steuerelements zu aktivieren.

2
Shahin Dohan

Dies ist ein bisschen ein Hacker, aber es funktioniert gut und erlaubt es Ihnen, Befehle zu verwenden und hinterher Code zu vermeiden. Dies hat auch den zusätzlichen Vorteil, dass der Befehl nicht ausgelöst wird, wenn Sie im leeren Bereich ScrollView doppelklicken (oder was auch immer Ihr Auslöser ist), vorausgesetzt, dass ListBoxItems nicht den gesamten Container ausfüllt.

Grundsätzlich erstellen Sie einfach eine DataTemplate für Ihre ListBox, die sich aus einer TextBlock zusammensetzt, und binden die Breite der TextBlock an die Breite der ListBox, stellen Sie die Ränder und die Auffüllung auf 0 und deaktivieren Sie das horizontale Scrollen (da die TextBlock über das Sichtbare hinausläuft Grenzen der ScrollView, die die horizontale Bildlaufleiste auslösen, sonst). Der einzige Fehler, den ich gefunden habe, ist, dass der Befehl nicht ausgelöst wird, wenn der Benutzer genau auf den Rand der ListBoxItem klickt, mit der ich leben kann.

Hier ist ein Beispiel:

<ListBox
    x:Name="listBox"
    Width="400"
    Height="150"
    ScrollViewer.HorizontalScrollBarVisibility="Hidden"
    ItemsSource="{Binding ItemsSourceProperty}"
    SelectedItem="{Binding SelectedItemProperty}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Padding="0" 
                        Margin="0" 
                        Text="{Binding DisplayTextProperty}" 
                        Width="{Binding ElementName=listBox, Path=Width}">
                <TextBlock.InputBindings>
                    <MouseBinding 
                        Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.SelectProjectCommand}" 
                                    Gesture="LeftDoubleClick" />
                </TextBlock.InputBindings>
            </TextBlock>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
0
manic_coder