Laden...

Events mit MVVM Pattern in WPF

Erstellt von Qt21580 vor 7 Jahren Letzter Beitrag vor 7 Jahren 3.889 Views
Q
Qt21580 Themenstarter:in
204 Beiträge seit 2005
vor 7 Jahren
Events mit MVVM Pattern in WPF

Seit stunden knabber ich an einem Problem das eigentlich gar keines sein dürfte.
Da ich noch sehr neu im Umgang mit WPF bin und das meine erste Anwnedung in MVVM ist habe ich eine Frage.....

Ich habe mir eine View mit einem ListView Control gebaut. Dieses ListView Control hat zwei Ansichten (Kartenansicht und Detailansicht) das funktioniert ja noch. In der Detailansicht habe ich eine Checkbox mittels eines Templetes eingebaut wenn ich das nun nanhake sollte ein Event ausgelöst werden nur geht das nicht und ich weiss nicht woran es liegen könnte.


        <DataTemplate x:Key="centralTile">
            <DockPanel DockPanel.Dock="Left" Height="72" Width="300">
                <Image Source="..\Images\Type_71.png"  Margin="6,6,6,9"/>
                <DockPanel DockPanel.Dock="Right" >
                    <TextBlock DockPanel.Dock="Top" Text="{Binding Sachnummer}" FontSize="16" FontWeight="Bold" HorizontalAlignment="Left" Margin="0,4,0,1" />
                    <TextBlock DockPanel.Dock="Top" Text="{Binding Benennung}" FontSize="12" HorizontalAlignment="Left" Margin="0,0,0,1" />
                    <TextBlock DockPanel.Dock="Top" Text="{Binding Anzahl}" FontSize="12" HorizontalAlignment="Left" Margin="0,0,0,1" />
                </DockPanel>
            </DockPanel>
        </DataTemplate>


Habe hier schon alles versucht was Dr.Google ausgespuckt hat nur leider ohne Erfolg. Mit dem Checked-Event funktionierts zwar nur ist das nicht MVVM Conform und überhaupt bin ich nicht im ViewModel wo ich die Liste befülle.
        <DataTemplate x:Key="checkbox">
            <!--<CheckBox x:Name="checkBox1" IsChecked="{Binding IsChecked, Mode=TwoWay}" Margin="0,1,1,1"/>-->
            <CheckBox x:Name="checkbox1" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListViewItem}}" Margin="0,1,1,1" Checked="CheckBox_Checked">
                

                <!--<i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <cmd:EventToCommand Command="{Binding Path=ListViewCheckedCommand, Mode=OneWay}" CommandParameter="{Binding IsChecked, ElementName=checkbox}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>-->
                <!--<i:InvokeCommandAction Command="{Binding ListViewCheckedCommand}" CommandParameter="" />
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Checked">
                        <i:InvokeCommandAction Command="{Binding Path=ListViewCheckedCommand}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>

                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Checked">
                        <helper:SetterAction PropertyName="Checked" Value="Checked" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                    <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <helper:CommandAction Command="{Binding Path=ListViewCheckedCommand}"  />
                    </i:EventTrigger>
                </i:Interaction.Triggers>-->
            </CheckBox>
        </DataTemplate>

        <GridView x:Key="gridView">
            <GridViewColumn CellTemplate="{StaticResource checkbox}" />
            <!--<GridViewColumn CellTemplate="{StaticResource checkbox}">
                <CheckBox x:Name="checkBox1" IsChecked="{Binding IsChecked, Mode=TwoWay}" Margin="0,1,1,1"/>
            </GridViewColumn>-->
            <GridViewColumn Header="Auftrags Nr" DisplayMemberBinding="{Binding AuftragLfdNr}"/>
            <GridViewColumn Header="Sachnummer" DisplayMemberBinding="{Binding Sachnummer}"/>
            <GridViewColumn Header="Benennung" Width="200" DisplayMemberBinding="{Binding Benennung}"/>
            <GridViewColumn Header="Abmessung" DisplayMemberBinding="{Binding Abmessung}"/>
            <GridViewColumn Header="Afo" DisplayMemberBinding="{Binding Afo}"/>
            <GridViewColumn Header="Termin" DisplayMemberBinding="{Binding Termin}"/>
            <GridViewColumn Header="Status" DisplayMemberBinding="{Binding Status}"/>
            <GridViewColumn Header="Anzahl" DisplayMemberBinding="{Binding Anzahl}"/>
            <GridViewColumn Header="Rep. Stufe" DisplayMemberBinding="{Binding RepStufe}"/>
            <GridViewColumn Header="Priorität" DisplayMemberBinding="{Binding Prio}"/>
            <GridViewColumn Header="Standort" DisplayMemberBinding="{Binding Standort}"/>
        </GridView>

        <local:PlainView x:Key="tileView" 
                   ItemTemplate="{StaticResource centralTile}" 
                   ItemWidth="200"/>

Ich weiss echt nicht mehr weiter.
Hoffe jemand kann mir helfen wie ich in einem DataTemplate einen Trigger auslöse oder die Checked Eigenschaft im ViewModel binde.

Hinweis von Abt vor 7 Jahren

In Zukunft bitte einen Forentitel wählen, mit dem der Leser auch etwas anfangen kann. Danke.

P
19 Beiträge seit 2016
vor 7 Jahren

Events solltest du mit MVVM auch vermeiden :

Du kannst den Trigger im Style der Checkbox setzen

 
 <Style.Triggers>
          <Trigger Property="IsChecked" Value="True" >
                 ...      
         </Trigger>
</Style.Triggers>

oder im DataTemplet

 
<DataTemplate.Triggers>
    <DataTrigger Binding="{Binding IsSelected}" Value="True">
                                                ...
     </DataTrigger>
</DataTemplate.Triggers>

Du könntest in Deinem ViewModel auch auf das PropertyChanged Event der Eigenschaft: 'IsSelected' warten.


...ctor()
this.PropertyChanged += VM_PropertyChanged
...

        private void VM_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case nameof(IsSelected):
                    RaiseSelectedChanged();
                    break;

                default:
                    break;
            }
        }


Q
Qt21580 Themenstarter:in
204 Beiträge seit 2005
vor 7 Jahren

Danke PottKafe,

Habe alle drei Möglichkeiten probiert nur leider funktioniert keine davon.
Was mir aber aufgefallen ist, wenn ich in der Datailansicht mehr als einen Eintrag auswähle mit der Strg - Taste nicht mit der Checkbox wird das SelectedItem nur einmal ausgelöst.


<DataTemplate x:Key="checkbox">
            <!--<CheckBox x:Name="checkBox1" IsChecked="{Binding IsChecked, Mode=TwoWay}" Margin="0,1,1,1"/>-->
            <CheckBox x:Name="checkbox1" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListViewItem}}" Margin="0,1,1,1">
                <!--<Trigger Property="IsChecked" Value="True"></Trigger>-->
            </CheckBox>
            <!--<DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=IsChecked}" Value="True" />
            </DataTemplate.Triggers>-->

Beim PropertyChanged Event komm kein IsSelected an weil das mit der Bindung an die Eigenschaft IsChecked eben nicht funktioniert.


<ListView Name="lv" ItemsSource="{Binding Path=WZS_JobCollection}" FontSize="12" SelectedItem="{Binding Path=SelectedWZSAuftragItem}" src:AuftragViewModel.ListView="{Binding ElementName=lv}">

es muss doch eine Möglichkeit geben so einen bescheuertes Event/Command auszulösen.

D
985 Beiträge seit 2014
vor 7 Jahren

Würde dir so etwas helfen?


<!-- NuGet Paket Expression.Interaction (Microsoft) -->
<UserControl x:Class="WpfApp1.th118161.MainView"
             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" 
             xmlns:local="clr-namespace:WpfApp1.th118161"
             
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <local:MainViewModel/>
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <Border Grid.Row="0">
            <ListView ItemsSource="{Binding Path=DataItems}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding SelectionChanged}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=SelectedItems}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="x">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}, Path=IsSelected}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Path=Id}" Width="30"/>
                        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" Width="100"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </Border>

        <!-- Nur zur Kontrolle -->

        <Border Grid.Row="1">
            <ListView ItemsSource="{Binding Path=SelectedItems}">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Path=Id}" Width="30"/>
                        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" Width="100"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </Border>
    </Grid>
</UserControl>


[PropertyChanged.ImplementPropertyChanged]
public class MainViewModel
{
    public MainViewModel( )
    {
        SelectionChanged = new SelectionChangedCommand( this );

        DataItems = new ObservableCollection<Data> {
            new Data{ Id = 1, Name="Name 1", },
            new Data{ Id = 2, Name="Name 2", },
            new Data{ Id = 3, Name="Name 3", },
            new Data{ Id = 4, Name="Name 4", },
            new Data{ Id = 5, Name="Name 5", },
        };
    }
    public ObservableCollection<Data> DataItems { get; }
    public ObservableCollection<Data> SelectedItems { get; internal set; }

    public ICommand SelectionChanged { get; }

    class SelectionChangedCommand
        : ICommand
    {
        private MainViewModel _parent;

        public SelectionChangedCommand( MainViewModel parent )
        {
            _parent = parent;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute( object parameter )
        {
            return true;
        }

        public void Execute( object parameter )
        {
            IList items = parameter as IList;
            if ( items != null )
                _parent.SelectedItems = new ObservableCollection<Data>( items.OfType<Data>( ) );
        }
    }
}

public class Data
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Folgende NuGet-Pakete habe ich dazu verwendet*Expression.Interaction *Fody *PropertyChanged.Fody

Eigentlich warst du gar nicht so weit weg von der Lösung. Nur dein Binding der CheckBox war nicht richtig:


<!-- Deine Version -->
<CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListViewItem}}" />
<!-- Funktionierende Version -->
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}, Path=IsSelected}"/>

Q
Qt21580 Themenstarter:in
204 Beiträge seit 2005
vor 7 Jahren

Danke für die Antwort Sir rufo, werde ich gleich mal probieren.

Interaktion ist das von expression blend oder.
Was sind die Pakete von fody bzw kann man die wo laden?

wird mit deiner Methode die Eigenschaft selectionchanged im Model aufgerufen?
Kannst du mir vielleicht noch einen Tipp geben wo ich zum Thema mvvm eine ordentliche Lektüre finde ( Gutes BUCH oder EBOOK .... ).

D
985 Beiträge seit 2014
vor 7 Jahren

Interaktion ist das von expression blend oder.

Wo die i:Interaction herkommen sieht man doch an dem xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity". Enthalten sind die im NuGet-Paket Expression.Interaction. Aber das solltest du doch schon in deinem Projekt haben, denn du verwendest die auch schon. 🤔

Was sind die Pakete von fody

Mit Fody kann man sich das lästige PropertyChanged-Boilerplate schenken indem man der Klasse einfach das Attribut [PropertyChanged.ImplementPropertyChanged] verpasst.

NuGet-Paket PropertyChanged.Fody
weches wiederum auf dem NuGet-Paket Fody aufsetzt (dieses Paket würde ich zuerst installieren und dann PropertyChanged.Fody)

bzw kann man die wo laden?

Ich dachte es wäre allgemein bekannt, dass man NuGet-Pakete bei NuGet erhält. Bzw. ganz einfach mit dem NuGet-Manager direkt aus Visual Studio heraus.

wird mit deiner Methode die Eigenschaft selectionchanged im Model aufgerufen?

Offensichtlich, denn die zweite ListView binde ich an SelectedItems vom ViewModel.

Kannst du mir vielleicht noch einen Tipp geben wo ich zum Thema mvvm eine ordentliche Lektüre finde ( Gutes BUCH oder EBOOK .... ).

Da bin ich der falsche Ansprechpartner ... 😁