2017-03-24 114 views
2

我正在創建一個數據網格,並在列標題中使用了過濾器。它有效,但我認爲這不是一個好方法。讓我告訴你的代碼,很簡單的例子:Window.Resources中的WPF DataTemplate綁定參數

<Window x:Class="TestDataGridApp.Views.MainWindow" 
     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:viewModels="clr-namespace:TestDataGridApp.ViewModels" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="300" Width="300"> 
    <Window.DataContext> 
     <viewModels:MainWindowViewModel /> 
    </Window.DataContext> 
    <Window.Resources> 
     <DataTemplate x:Key="DataGridHeader"> 
      <DockPanel> 
       <TextBlock DockPanel.Dock="Top" TextAlignment="Left" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> 
       <TextBox DockPanel.Dock="Top" Text="{Binding DataContext.FilterName, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/> 
      </DockPanel> 
     </DataTemplate> 
    </Window.Resources> 
    <Grid> 
     <DataGrid ItemsSource="{Binding ItemCollection}" AutoGenerateColumns="False"> 
      <DataGrid.ColumnHeaderStyle> 
       <Style TargetType="{x:Type DataGridColumnHeader}"> 
        <Setter Property="HorizontalContentAlignment" Value="Stretch" /> 
       </Style> 
      </DataGrid.ColumnHeaderStyle> 

      <DataGrid.Columns> 
       <DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" Width="60" MinWidth="60" MaxWidth="60" HeaderTemplate="{StaticResource DataGridHeader}"/> 
       <DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" Width="60" MinWidth="60" MaxWidth="60" HeaderTemplate="{StaticResource DataGridHeader}"/> 
      </DataGrid.Columns> 
     </DataGrid> 
    </Grid> 
</Window> 

視圖模型

namespace TestDataGridApp.ViewModels 
{ 
    using System; 
    using System.Collections.ObjectModel; 
    using System.ComponentModel; 
    using System.Windows.Data; 
    using TestDataGridApp.Entities; 
    using Prism.Mvvm; 
    public class MainWindowViewModel : BindableBase 
    { 
     private string _filterId; 
     private string _filterName; 
     private ObservableCollection<Item> _items = new ObservableCollection<Item>(); 

     public MainWindowViewModel() 
     { 
      for (int i = 1; i <= 100; ++i) 
      { 
       Items.Add(new Item() {Id = i, Name = $"Item{i}"}); 
      } 
     } 
     public string FilterId 
     { 
      get { return _filterId; } 
      set 
      { 
       SetProperty(ref _filterId, value); 
       TriggerFilters(); 
      } 
     } 
     public string FilterName 
     { 
      get { return _filterName; } 
      set 
      { 
       SetProperty(ref _filterName, value); 
       TriggerFilters(); 
      } 
     } 
     public ObservableCollection<Item> Items 
     { 
      get { return _items; } 
      set { SetProperty(ref _items, value); } 
     } 
     public ICollectionView ItemCollection => CollectionViewSource.GetDefaultView(Items); 

     private void TriggerFilters() 
     { 
      ItemCollection.Filter = o => FilterItem((Item)o); 
     } 
     private bool FilterItem(Item item) 
     { 
      try 
      { 
       bool checkId = false; 
       bool checkName = false; 

       int itemId = 0; 
       if (!string.IsNullOrEmpty(FilterId) && int.TryParse(FilterId, out itemId)) checkId = true; 
       if (!string.IsNullOrEmpty(FilterName)) checkName = true; 

       if (!checkId && !checkName) return true; 
       if (item == null) return false; 

       bool checkIdIsOk = (checkId && item.Id == int.Parse(FilterId) || !checkId); 
       bool checkNameIsOk = (checkName && item.Name.ToUpper().Contains(FilterName.ToUpper()) || !checkName); 
       if (checkIdIsOk && checkNameIsOk) return true; 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e); 
      } 
      return false; 
     } 
    } 
} 

該項目

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

基本上簡單的數據網格,2列。在每一列中都有一個帶有綁定過濾器的TextBox。每個過濾器都有自己的字段,所以在焦點丟失後,我可以通過所有過濾器過濾網格。

我的問題是..我有很多列。這是自定義的數據網格,因此您可以隨時添加和刪除列,並且有很多重複的代碼。基本上,這是重複的:

   <DataGridTextColumn.HeaderTemplate> 
        <DataTemplate> 
         <DockPanel> 
          <TextBlock DockPanel.Dock="Top" TextAlignment="Left" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> 
          <TextBox DockPanel.Dock="Top" 
            Text="{Binding DataContext.FilterId, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/> 
         </DockPanel> 
        </DataTemplate> 
       </DataGridTextColumn.HeaderTemplate> 

......僅此<TextBox DockPanel.Dock="Top" Text="{Binding DataContext.FilterId, ...正在改變不同的列。

所以,我想,我可以很容易地用此溶液代替它,但現在..我失去了綁定到視圖模型我的過濾器字段:

<Window.Resources> 
    <DataTemplate x:Key="DataGridHeader"> 
     <DockPanel> 
      <TextBlock DockPanel.Dock="Top" TextAlignment="Left" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> 
      <TextBox DockPanel.Dock="Top" Text="{Binding DataContext.FilterName, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/> 
     </DockPanel> 
    </DataTemplate> 
</Window.Resources> 
<Grid> 
    <DataGrid ItemsSource="{Binding ItemCollection}" AutoGenerateColumns="False"> 
     <DataGrid.ColumnHeaderStyle> 
      <Style TargetType="{x:Type DataGridColumnHeader}"> 
       <Setter Property="HorizontalContentAlignment" Value="Stretch" /> 
      </Style> 
     </DataGrid.ColumnHeaderStyle> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" Width="60" MinWidth="60" MaxWidth="60" HeaderTemplate="{StaticResource DataGridHeader}"/> 
      <DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" Width="60" MinWidth="60" MaxWidth="60" HeaderTemplate="{StaticResource DataGridHeader}"/> 
     </DataGrid.Columns> 
    </DataGrid> 
</Grid> 

SOO ..我在想,創建Dictionary用於過濾器,其中鍵將是列的名稱,值I將存儲當前過濾器(如果此時沒有過濾器,則爲null)。像..

<TextBox x:Name="Foo" DockPanel.Dock="Top" Text="{Binding DataContext.FiltersDictionary[Foo], RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=LostFocus}"/> 

但是,然後我必須爲一個文本框比丁上下文。我真的不知道這個解決方案..

我的問題是,如何在上述情況下創建DataTemplate的參數?

感謝您的幫助!

PS。這不是重複的。這個問題是關於「如何爲DataTemplate創建參數」。 「重複」的問題是關於字典作爲綁定 - 這個問題的潛在解決方案..雖然可能不是。正如另一位用戶所建議的,可能會有完全不同的更好的解決方案來解決這個問題兩件不同的事情。我很震驚,我必須解釋這個

+0

的[WPF結合具有x到詞典:名稱作爲關鍵字]可能的複製(HTTP:// stackoverflow.com/questions/42987402/wpf-binding-to-dictionary-with-xname-as-a-key) – Rekshino

+1

@Rekshino你是認真的嗎?它不是重複的。在這裏我問「如何爲DataTemplate創建一個參數」,另一個問題是關於「字典作爲綁定」。這個問題的答案可能與字典毫無關係。你甚至讀過這兩個問題嗎? –

回答

2

最簡單的方法是不只依靠xaml並添加一些代碼來幫助。例如使用Loaded事件您TextBox的是這樣的:

<DataTemplate x:Key="DataGridHeader"> 
    <DockPanel> 
     <TextBlock DockPanel.Dock="Top" TextAlignment="Left" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> 
     <TextBox DockPanel.Dock="Top" Loaded="OnFilterBoxLoaded" /> 
    </DockPanel> 
</DataTemplate> 

和設置,當它被裝載綁定:

private void OnFilterBoxLoaded(object sender, RoutedEventArgs e) { 
    var tb = (TextBox)sender; 
    // find column 
    DataGridColumnHeader parent = null; 
    DependencyObject current = tb; 
    do { 
     current = VisualTreeHelper.GetParent(current); 
     parent = current as DataGridColumnHeader; 
    } 
    while (parent == null); 
    // setup binding 
    var binding = new Binding(); 
    // use parent column header as name of the filter property 
    binding.Path = new PropertyPath("DataContext.Filter" + parent.Column.Header); 
    binding.Source = this; 
    binding.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus; 
    tb.SetBinding(TextBox.TextProperty, binding); 
} 

您可以使用附加屬性來實現相同的,但我不認爲這是在這種情況下需要。

+0

謝謝!我沒有想到這種方法。但那可以工作!雖然,我可能不能使用'parent.Column.Header',因爲它可能包含空格,括號等。 –

+0

那麼它定義將工作,因爲我發佈之前測試它:)至於頭 - 你可以使用任何其他屬性(可能綁定路徑)或創建附加屬性,並將其用於網格列以提供顯式過濾器名稱。如果您在使用附加屬性時遇到麻煩,我可以發佈一個示例。 – Evk

+0

爲何附屬物?我用你的解決方案,但用簡單的'DependencyProperty'我發佈這個答案。你能評論這是否正確?它的作品..我只是不明白爲什麼附加的財產? –

1

我用EVK溶液與DependencyProperty代替使用Header

<controls:FilterDataGridTextColumn FilterName="Name" Header="Name" Binding="{Binding Path=Name}" Width="200" HeaderTemplate="{StaticResource HeaderTemplate}" /> 

FilterDataGridTextColumn:

public class FilterDataGridTextColumn : DataGridTextColumn 
{ 
    public static readonly DependencyProperty FilterNameProperty = 
     DependencyProperty.Register("FilterName", typeof(string), typeof(FilterDataGridTextColumn)); 

    public string FilterName 
    { 
     get { return (string) GetValue(FilterNameProperty); } 
     set { SetValue(FilterNameProperty, value); } 
    } 
}