2014-11-04 46 views
0

我有以下数据网格WPF使用相同的DataTemplate不同的结合

<Window 
    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" mc:Ignorable="d" x:Class="VenusProductInfoQueryWPF.MainWindow" 
    Height="350" Width="646" WindowStyle="None" ResizeMode="NoResize" Topmost="False" MouseLeftButtonDown="Window_MouseLeftButtonDown" AllowsTransparency="True" WindowStartupLocation="CenterScreen" ShowInTaskbar="True"> 
    <Window.Resources>   
     <DataTemplate x:Key="DataTemplate1"> 
      <Button Tag="{Binding Name}" Content="Show" Click="LinkButton_Click"></Button> 
     </DataTemplate> 
     <DataTemplate x:Key="DataTemplate2"> 
      <Button Tag="{Binding Sex}" Content="Show" Click="LinkButton_Click"></Button> 
     </DataTemplate> 
    </Window.Resources>` 

    <Grid> 
     <DataGrid x:Name="MyDataGrid" HorizontalAlignment="Left" Margin="60,44,0,0" 
        VerticalAlignment="Top" Height="223" Width="402" AutoGenerateColumns="False" 
        AutoGeneratedColumns="MyDataGrid_AutoGeneratedColumns"> 
      <DataGrid.Columns> 
       <DataGridTextColumn Header="Age" Binding="{Binding Path=Age}"></DataGridTextColumn> 
       <DataGridTemplateColumn Header="Sex" CellTemplate="{StaticResource DataTemplate2}"/> 
       <DataGridTemplateColumn Header="Name" CellTemplate="{StaticResource DataTemplate1}"/> 
      </DataGrid.Columns> 
     </DataGrid> 
    </Grid> 
</Window> 

,并在代码隐藏我:

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Windows; 
using System.Windows.Media; 

namespace WpfApplication1 
{ 
public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     dataSource = new DataTable(); 
     dataSource.Columns.Add("Age"); 
     dataSource.Columns.Add("Name"); 
     dataSource.Columns.Add("Sex"); 

     AddNewRow(new object[] { 10, "wang", "Male" }); 
     AddNewRow(new object[] { 15, "huang", "Male" }); 
     AddNewRow(new object[] { 20, "gao", "Female" }); 

     dataGrid1.ItemsSource = dataSource.AsDataView(); 
    } 
} 
} 

DataTable中有超过30列(我只在这里写下2使问题变得更容易)。问题是:如果我想要在每一列中显示与不同数据源相同的模板样式,是否真的必须定义许多不同的数据模板(如DataTemplate1,DataTemplate2,...参见上文)将每个DataGridTemplateColumn的CellTemplate绑定到它?我可以定义一个数据模板,并在代码中或通过其他方式动态设置绑定?谢谢您的回答!

+0

每个按钮都有自己与相应属性的绑定,所以我不认为有一些方法可以在这里使用1个模板。虽然我们可以缩短你的模板,就像这样:

回答

0

有一种方式,但它是不漂亮,

为了简便起见,我使用的代码背后的视图模型,我已删除了异常处理和不必要的行。

我有你的对象数组转换成Person对象(对于应在解决方案后变得明显的原因)

public class Person 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 
    public string Sex { get; set; } 
} 

我已经将您的数据源到一个ObservableCollection这样的代码背后,是现在

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     Items = new ObservableCollection<Person>() 
     { 
      new Person {Age = 10, Name = "wang", Sex="Male"}, 
      new Person {Age = 15, Name = "huang", Sex="Male"}, 
      new Person {Age = 20, Name = "gao", Sex="Female"} 
     }; 
     ShowCommand = new DelegateCommand(ExecuteShowCommand, CanExecuteShowCommand); 
     InitializeComponent(); 
    } 

    private bool CanExecuteShowCommand(object arg) { return true; } 
    private void ExecuteShowCommand(object obj) { MessageBox.Show(obj != null ? obj.ToString() : "No Parameter received"); } 
    public DelegateCommand ShowCommand { get; set; } 
    public ObservableCollection<Person> Items { get; set; } 
} 

的DelegateCommand被定义为

public class DelegateCommand : ICommand 
{ 
    private Func<object, bool> _canExecute; 
    private Action<object> _execute; 

    public DelegateCommand(Action<object> execute, Func<object, bool> canExecute) 
    { 
     _canExecute = canExecute; 
     _execute = execute; 
    } 

    public bool CanExecute(object parameter) { return _canExecute.Invoke(parameter); } 
    void ICommand.Execute(object parameter) { _execute.Invoke(parameter); } 
    public event EventHandler CanExecuteChanged; 
} 

这允许在单个模板中使用Commands和CommandParameters。

然后,使用一个MultiValueConverter获取数据为每个按钮。它使用列的标题来查询使用反射的Person对象以获取所需的值,然后将其作为命令的参数传回。

public class GridCellToValueConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     object returnValue = null; 
     var person = values.First() as Person; 
     var propertyName = values[1] == DependencyProperty.UnsetValue ? string.Empty : (string)values[1]; 
     if ((person != null) && (!string.IsNullOrWhiteSpace(propertyName))) { returnValue = person.GetType().GetProperty(propertyName).GetValue(person); } 
     return returnValue; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } 
} 

的最后一块拼图是XAML

<Window x:Class="StackOverflow.Q26731995.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:q26731995="clr-namespace:StackOverflow.Q26731995" Title="MainWindow" Height="350" Width="525" 
     DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
    <Window.Resources> 
     <q26731995:GridCellToValueConverter x:Key="GridCell2Value" /> 
     <DataTemplate x:Key="ButtonColumnDataTemplate"> 
      <Button Content="Show" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=ShowCommand}"> 
       <Button.CommandParameter> 
        <MultiBinding Converter="{StaticResource GridCell2Value}"> 
         <Binding /> <!-- The person object --> 
         <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridCell}}" Path="Column.Header" /> <!-- The name of the field --> 
        </MultiBinding> 
       </Button.CommandParameter> 
      </Button> 
     </DataTemplate> 
    </Window.Resources> 

    <Grid> 
     <DataGrid x:Name="MyDataGrid" HorizontalAlignment="Left" Margin="60,44,0,0" ItemsSource="{Binding Path=Items}" VerticalAlignment="Top" Height="223" Width="402" AutoGenerateColumns="False"> 
      <DataGrid.Columns> 
       <DataGridTextColumn Header="Age" Binding="{Binding Path=Age}"></DataGridTextColumn> 
       <DataGridTemplateColumn Header="Sex" CellTemplate="{StaticResource ButtonColumnDataTemplate}"/> 
       <DataGridTemplateColumn Header="Name" CellTemplate="{StaticResource ButtonColumnDataTemplate}"/> 
      </DataGrid.Columns> 
     </DataGrid> 
    </Grid> 
</Window> 

你应该能够削减和过去的这个代码到一个新的WPF应用程序,看看它的工作。不是我没有照顾网格添加的AddNewItem行(因为我们没有关闭它)。

这可以通过对象数组的集合,而不是人的集合来完成。为此,您需要将DataGridCellsPanel传递给转换器,并将其与标题一起使用以计算所需值的索引。转换看起来像

<MultiBinding Converter="{StaticResource GridCell2Value}"> 
    <Binding /> <!-- The data --> 
    <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridCell}}" Path="Column.Header}" /> <!-- The name of the field --> 
    <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridCellsPanel}}" /> <!-- The panel that contains the row --> 
</MultiBinding> 

转换器代码将是沿线

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     object returnValue = null; 

     var data = values.First() as object[]; 
     var property = values[1] == DependencyProperty.UnsetValue ? string.Empty : (string)values[1]; 
     var panel = values[2] as DataGridCellsPanel; 
     var column = panel.Children.OfType<DataGridCell>().FirstOrDefault(c => c.Column.Header.Equals(property)); 
     if (column != null) 
     { 
      returnValue = data[panel.Children.IndexOf(column)]; 
     } 
     return returnValue; 
    } 

我希望这有助于。

+0

谢谢,我现在专注于其他项目,我会尽快尝试您的代码。 – DayongWang 2014-11-10 02:01:00