none
Button in einem ListViewItem RRS feed

  • Frage

  • Hallo Forum,

    ich wolle in einem ListViewItem einen Button mit einem Command platzieren, der aber nur für das gewählte Item ausgelöst wird. 
    Im Moment habe ich den Button auf dem ViewModel.

     Das aktiv selektierte ListViewItem ist Nr. 4. Der Cursor steht aber über der roten Fahne bei Nr. 5. Klicke ich nun, wird der Command für den Datensatz Nr. 4 ausgelöst und nicht für Nr. 5. Wohl weil dieser die eben aktuell markiert ist.

    Mein Command dazu sieht so aus.

    Command="{Binding Path=DataContext.CmdAgg[OnObjectClickedCommand],
                      RelativeSource={RelativeSource FindAncestor,
                      AncestorType={x:Type ListView}}}"
    

    Danke und Gruß
    Gerhard Ahrens

    Mittwoch, 12. August 2015 06:13

Antworten

  • Hi Gerhard,
    hier mal eine kleine Demo, wie ich das mache:

    XAML:

    <Window x:Class="WpfApplication1CS.Window02"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApplication1CS"
            mc:Ignorable="d"
            Title="Window02" Height="300" Width="300">
      <Window.Resources>
        <local:Window02VM x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{Binding Source={StaticResource vm}}">
        <Grid.RowDefinitions>
          <RowDefinition/>
          <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <ListView ItemsSource="{Binding View}">
          <ListView.View>
            <GridView>
              <GridViewColumn Header="Klick mich">
                <GridViewColumn.CellTemplate>
                  <DataTemplate>
                    <Button Content="{Binding ID}" Width="50"
                            Command="{Binding Source={StaticResource vm}, Path=Cmd}"
                            CommandParameter="{Binding ID}"/>
                  </DataTemplate>
                </GridViewColumn.CellTemplate>
              </GridViewColumn>
              <GridViewColumn Header="Datensatzinhalt" Width="100">
                <GridViewColumn.CellTemplate>
                  <DataTemplate>
                    <TextBlock Text="{Binding Info}"/>
                  </DataTemplate>
                </GridViewColumn.CellTemplate>
              </GridViewColumn>
            </GridView>
          </ListView.View>
        </ListView>
        <TextBlock Grid.Row="1" Text="{Binding Inhalt}"/>
      </Grid>
    </Window>

    Dazu der ViewModel:

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows.Data;
    using System.Windows.Input;
    
    namespace WpfApplication1CS
    {
      class Window02VM : INotifyPropertyChanged
      {
        ObservableCollection<Window02Data> Liste;
        CollectionViewSource cvs;
    
        public ICollectionView View
        {
          get
          {
            if (Liste == null)
            {
              Liste = new ObservableCollection<Window02Data>();
              cvs = new CollectionViewSource();
              for (int i = 1; i < 10; i++)
              {
                Liste.Add(new Window02Data() { ID = i, Info = string.Format("Zeile {0}", i) });
              }
              cvs.Source = Liste;
            }
            return cvs.View;
          }
        }
    
        public ICommand Cmd
        {
          get
          {
            return new RelayCommand(CmdExec);
          }
        }
    
        private void CmdExec(object obj)
        {
          Inhalt = string.Format("Geklickte Zeile: {0}", obj);
        }
    
        private string _inhalt;
        public string Inhalt
        {
          get
          {
            return this._inhalt;
          }
          set
          {
            if (this._inhalt != value)
            {
              this._inhalt = value;
              OnPropertyChanged();
            }
          }
        }
    
        #region OnPropertyChanged
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propname = "")
        {
          if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propname));
        }
    
        #endregion
    
        public class Window02Data
        {
          public int ID { get; set; }
          public string Info { get; set; }
        }
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    Freitag, 14. August 2015 03:33

Alle Antworten

  • Hi Gerhard,
    ich binde in diesem Fall die Buttons aller Datenobjekte über RelayCommand an die gleiche Eigenschaft. Um jetzt zu erkennen, bei welchem Datensatz der Button geklickt wurde, binde ich die CommandParameter-Eigenschaft an eine eindeutige Identifikation, z.B. die ID des Datenobjektes. In der Ereignismethode, die dann von RelayCommand aufgerufen wird, wird über die CommandParameter-Eigenschaft die Identifikation übergeben und kann in der Command-Methode genutzt werden. Wichtig dabei ist, dass RelayCommand den CommandParameter-Inhalt auch weiterleitet. Ein Beispiel kann ich Dir posten, wenn Du mal schreibst, in welcher Programmiersprache.

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    Mittwoch, 12. August 2015 06:30
  • Hallo zusammen,

    @Gerhard: Ich finde die Bedienung im ersten Moment etwas kontraintuitiv. Wenn ich innerhalb eines Elements auf einen Button klicke, dann erwarte ich das die Aktion auch etwas mit diesem Element zu tun hat. Es gilt natürlich Fälle wo man das durchaus so machen kann, beispielsweise beim zuordnen von Items zueinander.

    Sollte der Button der angeklickt wurde jedoch egal sein und es zählt nur das überhaupt einer gedrückt wurde, so würde ich den Button komplett auf dem ListView heraus nehmen und daneben setzen.


    Tom Lambert - .NET (C#) MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Mittwoch, 12. August 2015 06:58
    Moderator
  • Hallo Tom, Peter,

    das ist ja genau mein Problem. Das ListView/DataGrid unterscheidet intern zwischen selektierten und nicht selektierten Item. In Prinzip will ich auf jeden Button klicken können. Es reagiert aber immer nur der tatsächlich selektierte Item mit dem Button, egal wo ich in klicke. Ich habe nun mal tum Test einen Command in meine Entität zusätzlich aufgenommen.

    [DebuggerDisplay("CASE = {CASE}, TITLE = {TITLE}")] public class MarketTechnologyEntity : BaseModel { #region EventHandler Definition public static EventHandler<EntityEventArgs> SelectCaseHandler; public static EventHandler<EntityEventArgs> ChangeFlagColorEventHandler; #endregion EventHandler Definition #region OnSelectCaseHandler private static void OnSelectCaseHandler(EntityEventArgs e) { EventHandler<EntityEventArgs> myeventhandler = SelectCaseHandler; if (myeventhandler != null) { myeventhandler(typeof(MarketTechnologyEntity), e); } } private RelayCommand _OnObjectClickedCommand; public ICommand OnObjectClickedCommand { get { if (_OnObjectClickedCommand == null) { _OnObjectClickedCommand = new RelayCommand( exeParam => this.OnObjectClickedHandler(exeParam), canParam => true); } return _OnObjectClickedCommand; } } private object OnObjectClickedHandler(object exeParam) { EntityEventArgs e = new EntityEventArgs(); e.Entity = this; OnSelectCaseHandler(e); return true; } #endregion OnSelectCaseHandler

    // weitere Properties

    }

    Das funktioniert nun zwar, ist aber keine vernünftige Lösung. Einfach nicht gut so. Wie geht das aber nun besser.

    Gruß
    Gerhard


    Mittwoch, 12. August 2015 10:20
  • Hi Gerhard,
    hier mal eine kleine Demo, wie ich das mache:

    XAML:

    <Window x:Class="WpfApplication1CS.Window02"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApplication1CS"
            mc:Ignorable="d"
            Title="Window02" Height="300" Width="300">
      <Window.Resources>
        <local:Window02VM x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{Binding Source={StaticResource vm}}">
        <Grid.RowDefinitions>
          <RowDefinition/>
          <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <ListView ItemsSource="{Binding View}">
          <ListView.View>
            <GridView>
              <GridViewColumn Header="Klick mich">
                <GridViewColumn.CellTemplate>
                  <DataTemplate>
                    <Button Content="{Binding ID}" Width="50"
                            Command="{Binding Source={StaticResource vm}, Path=Cmd}"
                            CommandParameter="{Binding ID}"/>
                  </DataTemplate>
                </GridViewColumn.CellTemplate>
              </GridViewColumn>
              <GridViewColumn Header="Datensatzinhalt" Width="100">
                <GridViewColumn.CellTemplate>
                  <DataTemplate>
                    <TextBlock Text="{Binding Info}"/>
                  </DataTemplate>
                </GridViewColumn.CellTemplate>
              </GridViewColumn>
            </GridView>
          </ListView.View>
        </ListView>
        <TextBlock Grid.Row="1" Text="{Binding Inhalt}"/>
      </Grid>
    </Window>

    Dazu der ViewModel:

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows.Data;
    using System.Windows.Input;
    
    namespace WpfApplication1CS
    {
      class Window02VM : INotifyPropertyChanged
      {
        ObservableCollection<Window02Data> Liste;
        CollectionViewSource cvs;
    
        public ICollectionView View
        {
          get
          {
            if (Liste == null)
            {
              Liste = new ObservableCollection<Window02Data>();
              cvs = new CollectionViewSource();
              for (int i = 1; i < 10; i++)
              {
                Liste.Add(new Window02Data() { ID = i, Info = string.Format("Zeile {0}", i) });
              }
              cvs.Source = Liste;
            }
            return cvs.View;
          }
        }
    
        public ICommand Cmd
        {
          get
          {
            return new RelayCommand(CmdExec);
          }
        }
    
        private void CmdExec(object obj)
        {
          Inhalt = string.Format("Geklickte Zeile: {0}", obj);
        }
    
        private string _inhalt;
        public string Inhalt
        {
          get
          {
            return this._inhalt;
          }
          set
          {
            if (this._inhalt != value)
            {
              this._inhalt = value;
              OnPropertyChanged();
            }
          }
        }
    
        #region OnPropertyChanged
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propname = "")
        {
          if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propname));
        }
    
        #endregion
    
        public class Window02Data
        {
          public int ID { get; set; }
          public string Info { get; set; }
        }
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    Freitag, 14. August 2015 03:33
  • Hallo Peter,

    es hat leider mit der Antwort etwas länger gedauert. Ja, so geht es noch einfacher. Ich hatte wohl zu kompliziert gedacht, da ich angenommen hatte, der Button wäre ein Teil meiner Entität. Ist es aber nicht.

    Also, danke.

    Gruß

    Gerhard Ahrens

    Dienstag, 18. August 2015 09:51