筛选DataGrid列并将对象传递给ViewModel

时间:2019-11-17 17:58:34

标签: wpf mvvm binding datagrid

我收到此错误:

  

System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“ String”(HashCode = 62725275)上找不到“ OpenPopupCommand”属性。 BindingExpression:Path = OpenPopupCommand; DataItem ='字符串'

当我在命令中添加参数时:

OpenPopupCommand = new RelayParamCommand((e) => PopupVisibility(FilterButton) );

VM:

private void PopupVisibility(object sender)
{
    Console.WriteLine(sender.ToString());
    PopupVisible ^= true;
}

认为,我向“自动生成的Datagrid标头”添加了“过滤器按钮”。现在,我想在单击按钮时打开弹出窗口。但是认为这是行不通的,因为我必须将x:Name按钮传递给Popup PlacementTarget参数。

<Page.DataContext>
    <PDB:UsersViewModel x:Name="vm"/>
</Page.DataContext>


<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <!--Page Header info content-->
    <Grid Grid.Row="0">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="2" Text="{Binding ElementName=userPage, Path=Name}"/>
            <TextBlock Margin="2" Text="{Binding SelectedUser.Name}"/>
            <TextBlock Margin="2" Text="{Binding ElementName=myGrd, Path=CurrentColumn.DisplayIndex}"/>
            <Button x:Name="mybtn" 
                    Content="{Binding Filters.Count, Mode=OneWay}" 
                    Visibility="{Binding Filters.Count, Converter={Wpf:VisibilityConverter}}" 
                    />
        </StackPanel>
    </Grid>
    <!--Datagrid content-->
    <DataGrid x:Name="myGrd" 

              SelectionMode="Single"    
              SelectionUnit="Cell"
              CurrentItem="{Binding SelectedUser, Mode=TwoWay}"
              CurrentColumn="{Binding CurrentColumn, Mode=TwoWay}"
              IsReadOnly="True"
              Grid.Row="1" 
              ItemsSource="{Binding FilteredUserList}" 
              AutoGenerateColumns="True"             
              CanUserAddRows="False"
              >
        <DataGrid.Resources>

            <!--Popup-->
            <ContextMenu x:Key="ContextMenu">
                <ContextMenu.Items>
                    <MenuItem Header="Filter by Selection" Command="{Binding IncludeCommand, Source={x:Reference vm}}"/>
                    <MenuItem Header="Filter exclude Selection" Command="{Binding ExcludeCommand, Source={x:Reference vm}}"/>
                    <MenuItem Header="Remove all Filters" Command="{Binding RemoveAllFiltersCommand, Source={x:Reference vm}}" Visibility="{Binding Filters.Count, Source={x:Reference vm}, Converter={Wpf:VisibilityConverter}}"/>
                </ContextMenu.Items>
            </ContextMenu>

            <!--Custom Datagrid header View-->               
            <Style TargetType="DataGridColumnHeader" x:Name="FilterHeader">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <StackPanel>
                                <TextBox Margin="0,0,0,10" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
                                <StackPanel Orientation="Horizontal">
                                    <TextBlock Text="{Binding}" HorizontalAlignment="Center"/>
                                    <Button Name="FilterButton" 
                                            Content="[-F-]" 
                                            Command="{Binding OpenPopupCommand}" 
                                            CommandParameter="{Binding ElementName=FilterButton}"/>
                                    <Popup Name="MyPopup" 
                                           StaysOpen="False" 
                                           Placement="Right" 
                                           IsOpen="{Binding PopupVisible}"
                                           PlacementTarget="{Binding FilterButton}">
                                        <Border Background="White" BorderBrush="Black" Padding="5" BorderThickness="2" CornerRadius="5">
                                            <StackPanel Orientation="Vertical">

                                            </StackPanel>
                                        </Border>
                                    </Popup> 
                                </StackPanel>
                            </StackPanel>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </DataGrid.Resources>

        <DataGrid.CellStyle>
            <Style TargetType="DataGridCell">
                <Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
            </Style>
        </DataGrid.CellStyle>
    </DataGrid>

</Grid>

通过这种方法,我想将clicked按钮作为参数传递给VM,并将其绑定到Popup PlacementTarget参数。我做错了什么?我可以传递点击按钮参数吗?我知道将视图传递给vm时会打破mvvm规则,但是当我不想在datagrid中定义每一列时如何实现我想要的。谢谢

3 个答案:

答案 0 :(得分:1)

您的绑定将DataContext的{​​{1}}作为源,即DataGridColumnHeader的值。在您的情况下,该值为DataGridColumn.Header,而不是预期的视图模型。这就是为什么您的绑定无法解析并且您收到错误消息的原因(准确地告诉您这一点)。

要修复绑定,您必须找到具有必需的string的下一个父元素,我假定DataContext

DataGrid

检查代码时,我可以看到绑定到<Button Name="FilterButton" Content="[-F-]" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.OpenPopupCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/> 的命令是 togging Button属性。因此,我建议从视图模型中删除与视图相关的代码,并将PopupVisible替换为直接绑定到Button的{​​{1}}:

ToggleButton

要获取单个列中包含的所有行的单元格值,其中列标题值是参数并映射到属性名称,则需要进行反射。您需要将Popup.IsOpen绑定到结果列值集合,该集合使用 <!--Custom Datagrid header View--> <Style TargetType="DataGridColumnHeader"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <StackPanel> <TextBox Margin="0,0,0,10" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" /> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding}" HorizontalAlignment="Center" /> <ToggleButton Name="FilterButton" Content="[-F-]" /> <Popup Name="MyPopup" StaysOpen="False" Placement="Right" IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}" PlacementTarget="{Binding ElementName=FilterButton}"> <Border Background="White" BorderBrush="Black" Padding="5" BorderThickness="2" CornerRadius="5"> <StackPanel Orientation="Vertical"> </StackPanel> </Border> </Popup> </StackPanel> </StackPanel> </DataTemplate> </Setter.Value> </Setter> </Style> 向项目添加ListView。最终版本应如下:

ItemTemplate

要将过滤器列表映射到实际项目,需要使用其他类型来保存信息:

Predicate.cs

CheckBox

您还需要为过滤器视图添加一个集合,并为视图模型添加一个包含项的索引:

UsersViewModel.cs

<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader">
  <Setter Property="ContentTemplate">
    <Setter.Value>
      <DataTemplate>
        <StackPanel>
          <TextBox Margin="0,0,0,10"
                   Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
          <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding RelativeSource={RelativeSource Self}}"
                       HorizontalAlignment="Center" />
            <ToggleButton Name="FilterButton"
                          Content="[-F-]"
                          Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.GenerateFilterViewItemsCommand)}"
                          CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridColumnHeader}, Path=Content}" />
            <Popup Name="MyPopup"
                   StaysOpen="False"
                   Placement="Right"
                   IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
                   PlacementTarget="{Binding ElementName=FilterButton}">
              <Border Background="White"
                      BorderBrush="Black"
                      Padding="5"
                      BorderThickness="2"
                      CornerRadius="5">
                <ListView
                  ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.FilterViewItems)}">
                  <ListView.ItemTemplate>
                    <DataTemplate>
                      <StackPanel Orientation="Horizontal">
                        <CheckBox IsChecked="{Binding IsIncluded, Mode=OneWayToSource}"
                                  Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.IncludeItemCommand)}"
                                  CommandParameter="{Binding}" />
                        <ContentPresenter Content="{Binding CellValue}" />
                      </StackPanel>
                    </DataTemplate>
                  </ListView.ItemTemplate>
                </ListView>
              </Border>
            </Popup>
          </StackPanel>
        </StackPanel>
      </DataTemplate>
    </Setter.Value>
  </Setter>
</Style>

答案 1 :(得分:0)

尝试以下方法:

为页面或DataGrid命名,并将Binding更改为

<MenuItem Command="{Binding Path=DataContext.IncludeCommand, Source={x:Reference myPage}}"/>

答案 2 :(得分:0)

由于您正在自动生成列,因此我强烈怀疑它试图在OpenPopupCommand的项目的字符串属性之一中找到FilteredUserList