如何编写此ItemsControl以便WPF将使用绑定为输出网格生成列

时间:2015-06-23 05:45:17

标签: wpf binding

下面的代码是一个主要工作的控件,可用于在包含两行的表中显示对象。第一行是标题行,第二行显示数据。可以将显示的对象的属性定义为控件的项目。每个项目在生成的表格中显示为一列。我不想使用反射来实现此控件。这个问题与反思无关。

控件有两个问题:

1。)它不是用“WPF格式”编写的(因为没有更好的描述),因为我在代码中创建对象而不是让绑定引擎完成工作。我之所以编码的原因是因为我不知道如何正确编码。注意我正在为控件的ItemsPanel使用一个网格(应该是什么)。我想使用网格,因为我希望输出是带有边框的表格形式。对于Items控件中的每个Item,我需要绑定到网格的第一行和第二行中的两个TextBlock。我还需要添加列,以便Items属性中的每个Item都显示在一个新列中。

2。)BuildDataCell方法中创建的绑定不起作用。如果1.)已解决,则很可能会解决此问题。在不太可能的情况下,控件不能以WPF形式编写,我想按原样使用它,如果我可以使绑定工作。

下面的文章显示了我在研究这个问题时遇到的几个StackOverflow问题。它要么不回答我的问题,要么回答它,但我不知道为什么。 http://drwpf.com/blog/2009/05/12/itemscontrol-l-is-for-lookless/#TemplatingAnItemsControl 这篇文章也很相关,但我不能将它应用于这个问题: http://blog.scottlogic.com/2010/11/15/using-a-grid-as-the-panel-for-an-itemscontrol.html

InfoTable.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace InfoTableHost
{
public class InfoTableItem : DependencyObject
{
    public String Header
    {
        get { return (String)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }

    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(String), typeof(InfoTableItem), new PropertyMetadata(String.Empty));



    public string Data
    {
        get { return (string)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(string), typeof(InfoTableItem), new PropertyMetadata(String.Empty));


    #region INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged([CallerMemberNameAttribute] string propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}


public class InfoTable : ItemsControl
{

    public enum ColumnSizeModes
    {
        AutoFitGrowLast,         // Shrink to fit except last column which expands to fill parent.
        UniformWidth,           // All Columns are the same width.  Table expands to fill parent
        AutoFit                // Shrink to fit.  Table does not expand to fill parent    
    }


    public ColumnSizeModes ColumnSizeMode
    {
        get { return (ColumnSizeModes)GetValue(ColumnSizeModeProperty); }
        set { SetValue(ColumnSizeModeProperty, value); }
    }

    public static readonly DependencyProperty ColumnSizeModeProperty =
        DependencyProperty.Register("ColumnSizeMode", typeof(ColumnSizeModes), typeof(InfoTable), new PropertyMetadata(ColumnSizeModes.AutoFitGrowLast));




    public Style HeaderCellStyle
    {
        get { return (Style)GetValue(HeaderCellStyleProperty); }
        set { SetValue(HeaderCellStyleProperty, value); }
    }

    public static readonly DependencyProperty HeaderCellStyleProperty =
        DependencyProperty.Register("HeaderCellStyle", typeof(Style), typeof(InfoTable), new PropertyMetadata(null));



    public Style HeaderTextStyle
    {
        get { return (Style)GetValue(HeaderTextStyleProperty); }
        set { SetValue(HeaderTextStyleProperty, value); }
    }

    public static readonly DependencyProperty HeaderTextStyleProperty =
        DependencyProperty.Register("HeaderTextStyle", typeof(Style), typeof(InfoTable), new PropertyMetadata(null));



    public Style DataCellStyle
    {
        get { return (Style)GetValue(DataCellStyleProperty); }
        set { SetValue(DataCellStyleProperty, value); }
    }

    public static readonly DependencyProperty DataCellStyleProperty =
        DependencyProperty.Register("DataCellStyle", typeof(Style), typeof(InfoTable), new PropertyMetadata(null));


    public Style DataTextStyle
    {
        get { return (Style)GetValue(DataTextStyleProperty); }
        set { SetValue(DataTextStyleProperty, value); }
    }

    public static readonly DependencyProperty DataTextStyleProperty =
        DependencyProperty.Register("DataTextStyle", typeof(Style), typeof(InfoTable), new PropertyMetadata(null));

    private bool isTemplateApplied;
    private Grid RootGrid;

    static InfoTable()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(InfoTable), new FrameworkPropertyMetadata(typeof(InfoTable)));
    }

    public InfoTable()
    {
        Loaded += InfoTable_Loaded;
    }

    void InfoTable_Loaded(object sender, RoutedEventArgs e)
    {
        if (!isTemplateApplied)
            return;

        Loaded -= InfoTable_Loaded;

        RootGrid = (Grid)GetTemplateChild("root");

        for (int i = 0; i < Items.Count; i++)
        {
            ColumnDefinition cd = new ColumnDefinition();
            if (ColumnSizeMode == ColumnSizeModes.UniformWidth || (ColumnSizeMode == ColumnSizeModes.AutoFitGrowLast && i == Items.Count - 1))
                cd.Width = new GridLength(1, GridUnitType.Star);
            else
                cd.Width = new GridLength(1, GridUnitType.Auto);

            RootGrid.ColumnDefinitions.Add(cd);
        }

        RootGrid.RowDefinitions.Add(new RowDefinition());
        RootGrid.RowDefinitions.Add(new RowDefinition());

        foreach (InfoTableItem item in Items.Cast<InfoTableItem>())
        {
            int index = Items.IndexOf(item);
            RootGrid.Children.Add(BuildHeaderCell(item, index));
            RootGrid.Children.Add(BuildDataCell(item, index));
        }
    }

    public override void OnApplyTemplate()
    {

        base.OnApplyTemplate();

        if (isTemplateApplied)
            return;

        isTemplateApplied = true;
    }

    private Border BuildHeaderCell(InfoTableItem item, int index)
    {
        Border b = new Border { Style = HeaderCellStyle };
        TextBlock t = new TextBlock { Style = HeaderTextStyle, Text = item.Header };
        b.SetValue(Grid.ColumnProperty, index);
        b.Child = t;
        return b;
    }

    private Border BuildDataCell(InfoTableItem item, int index)
    {
        Border b = new Border { Style = DataCellStyle };
        TextBlock t = new TextBlock { Style = DataTextStyle };
        Binding binding = new Binding { Source = Items[index], Path = new PropertyPath("Data"), UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
        t.SetBinding(TextBlock.TextProperty, binding);
        b.SetValue(Grid.ColumnProperty, index);
        b.SetValue(Grid.RowProperty, 1);
        b.Child = t;
        return b;
    }

}
}

Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:InfoTableHost">


    <Style TargetType="{x:Type local:InfoTable}">
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Margin" Value="0"/>
        <Setter Property="Padding" Value="0"/>
        <Setter Property="VerticalAlignment" Value="Top"/>
        <Setter Property="HeaderCellStyle">
            <Setter.Value>
                <Style TargetType="Border">
                    <Setter Property="Margin" Value="0" />
                    <Setter Property="Padding" Value="4" />
                    <Setter Property="Background" Value="Gray"/>
                    <Setter Property="BorderBrush" Value="Black"/>
                    <Setter Property="BorderThickness" Value=".5,.5,.5,.5"/>
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="HeaderTextStyle">
            <Setter.Value>
                <Style TargetType="TextBlock">
                    <Setter Property="Foreground" Value="White"/>
                </Style>
            </Setter.Value>
        </Setter>

        <Setter Property="DataCellStyle">
            <Setter.Value>
                <Style TargetType="Border">
                    <Setter Property="Margin" Value="0" />
                    <Setter Property="Padding" Value="4" />
                    <Setter Property="Background" Value="DarkGray"/>
                    <Setter Property="BorderBrush" Value="Black"/>
                    <Setter Property="BorderThickness" Value=".5,0,.5,.5"/>
                </Style>
            </Setter.Value>
        </Setter>

        <Setter Property="DataTextStyle">
            <Setter.Value>
                <Style TargetType="TextBlock">
                    <Setter Property="Foreground" Value="White"/>
                </Style>
            </Setter.Value>
        </Setter>

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:InfoTable}">
                    <Border Background="{TemplateBinding Background}" 
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Padding="{TemplateBinding Padding}"
                            VerticalAlignment="{TemplateBinding VerticalAlignment}">

                        <Grid VerticalAlignment="Top" x:Name="root" Margin="0">

                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

MainWindow.xaml(主页)

<Window x:Class="InfoTableHost.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:InfoTableHost"
        Title="MainWindow" Height="350" Width="525" Padding="0">
    <StackPanel DataContext="{Binding Observation}" >
        <StackPanel.Resources>
            <Style TargetType="TextBlock">
                <Setter Property="FontSize" Value="18"/>
                <Setter Property="Foreground" Value="Black"/>
            </Style>
        </StackPanel.Resources>
        <local:InfoTable>
            <local:InfoTableItem Header="Unbound Header" Data="Unbound Data"/>
            <local:InfoTableItem Header="Observation Date" Data="{Binding ObSDate}"/>
            <local:InfoTableItem Header="Close" Data="{Binding Close}"/>
        </local:InfoTable>
        <TextBlock>This TextBlock Binding Works: <TextBlock Text="{Binding ObsDate}" /></TextBlock>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace InfoTableHost
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public Observation Observation { get; set; }

        public MainWindow()
        {

            InitializeComponent();
            DataContext = this;
            Observation = new Observation { ObsDate = DateTime.Now, Close = 2201.55m };
        }
    }

    public class Observation : INotifyPropertyChanged
    {
        private DateTime _ObsDate;
        public DateTime ObsDate 
        {
            get { return _ObsDate; }
            set
            {
                if (_ObsDate != value)
                {
                    _ObsDate = value;
                    RaisePropertyChanged();
                }
            }
        }


        private decimal _Close;
        public decimal Close
        {
            get { return _Close; }
            set
            {
                if (_Close != value)
                {
                    _Close = value;
                    RaisePropertyChanged();
                }
            }
        }


        #region INotifyPropertyChanged implementation
        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged([CallerMemberNameAttribute] string propertyName = "")
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion


    }

}

1 个答案:

答案 0 :(得分:1)

事实证明问题在于InfoTableItem类。显然,DependencyObject不继承父控件的DataContext,因此绑定没有DataContext。要解决此问题,请使InfoTableItem继承自FrameworkElement并且您正在开展业务:

public class InfoTableItem : FrameworkElement
{
   ...
}