WPF datagrid header text binding

后端 未结 9 2279
执念已碎
执念已碎 2020-11-27 18:01

The column header of the DataGrid is not a FrameWork element for some reason, and so you cannot use bindings to set things like the header text. Please correct me if that is

9条回答
  •  爱一瞬间的悲伤
    2020-11-27 18:38

    My solution allows writing a single line in DataGridColumn with the name of the property that need to bind. He has the following features:

    • There is support for DataGridTextColumn
    • There is support for DataGridTemplateColumn
    • Set StringFormat for each column
    • Specify a static value for the StringFormat
    • Fully complies with MVVM pattern

    Example, which is below, includes StringFormat (he should stand before the PropertyPath):

     
    

    Equivalent to a this line:

    
    

    Who need more examples of solutions and features, please read below.

    Link for the sample project.


    Notes about the solution

    From all the solutions that I have seen earlier, the easiest for me turned out to be this example:

    
        
            
                
             
        
            
    

    Please pay attention to DataGridTextColumn.HeaderTemplate, if was used DataGridTextColumn.Header, then for .NET framework below version 4.5 and for Silverlight would produce an exception:

    Header property does not support UIElements

    It would seem that what is necessary? I wanted to find a solution that would allow to write a single line in DataGridColumn with the name of the property that need to bind.

    And here's what happened:

    This construction similar to this:

    
    

    Also is possible to use StringFormat for each column like this:

    
    

    And there is the ability to specify a static value for the StringFormat:

    Here is the original DataTemplate, which is dynamically set to the column:

    
        
    
    

    In order to RelativeSource did not depend on the type of DataContext, I took great solution from Mr.Bruno.

    In this case, DataGridCellsPanel contains the correct DataContext, which is set for a parent DataGrid.

    Below is the basic code that is performed all the magic:

    IsSetHeader PropertyChanged handler

    private static void IsSetHeader(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var textColumn = sender as DataGridTextColumn;
        var templateColumn = sender as DataGridTemplateColumn;
        string path = e.NewValue as string;
    
        if ((textColumn == null) & (templateColumn == null)) 
        {
            return;
        }
    
        if (String.IsNullOrEmpty(path) == false)
        {
            currentStringFormat = ReturnStringFormat(textColumn, templateColumn);
            dataTemplate = CreateDynamicDataTemplate(path, currentStringFormat);
    
            if (dataTemplate != null)
            {
                if (textColumn != null)
                    textColumn.HeaderTemplate = dataTemplate;
    
                if (templateColumn != null)
                    templateColumn.HeaderTemplate = dataTemplate;
            }
        }
    }
    

    CreateDynamicDataTemplate

    private static DataTemplate CreateDynamicDataTemplate(string propertyPath, string stringFormat)
    {
        var pc = new ParserContext();
        MemoryStream sr = null;
    
        string xaml = GetXamlString(propertyPath, stringFormat);            
        sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
    
        pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
        pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
    
        return XamlReader.Load(sr, pc) as DataTemplate;
    }
    

    GetXamlString

    private static string GetXamlString(string propertyPath, string stringFormat)
    {
        #region Original PropertyPath for TextBlock
    
        // {Binding Path=DataContext.YourProperty, RelativeSource={RelativeSource AncestorType={x:Type DataGridCellsPanel}}}"
        // Thanks to Bruno (https://stackoverflow.com/users/248118/bruno) for this trick
    
        #endregion
    
        var sb = new StringBuilder();
    
        sb.Append("");
    
        return sb.ToString();
    }
    

    StringFormat must appear before the PropertyPath, because it is optional. In order to for columns, who did not have it is not an exception occurs, I registered try-catch in GetStringFormat:

     public static string GetStringFormat(DependencyObject DepObject)
     {
        try
        {
            return (string)DepObject.GetValue(StringFormatProperty);
        }
    
        catch 
        {
            return String.Empty;
        }
     }
    

    Plus: do not write in methods try-catch block, that are trying to get the value.

    Minus: The minus for every missed StringFormat exception will be generated once when the program starts. If it is critical for you, you can always specify the StringFormat="null" for the column.

    Just in case, show the full code of project:

    public static class DataGridHeader
    {
        #region Private Section
    
        private static string textColumnStringFormat = null;
        private static string templateColumnStringFormat = null;
        private static string currentStringFormat = null;
        private static DataTemplate dataTemplate = null;
    
        #endregion
    
        #region PropertyPath DependencyProperty
    
        public static readonly DependencyProperty PropertyPathProperty;
    
        public static void SetPropertyPath(DependencyObject DepObject, string value)
        {
            DepObject.SetValue(PropertyPathProperty, value);
        }
    
        public static string GetPropertyPath(DependencyObject DepObject)
        {
            return (string)DepObject.GetValue(PropertyPathProperty);
        }
    
        #endregion
    
        #region StringFormat DependencyProperty
    
        public static readonly DependencyProperty StringFormatProperty;
    
        public static void SetStringFormat(DependencyObject DepObject, string value)
        {
            DepObject.SetValue(StringFormatProperty, value);
        }
    
        public static string GetStringFormat(DependencyObject DepObject)
        {
            try
            {
                return (string)DepObject.GetValue(StringFormatProperty);
            }
    
            catch 
            {
                return String.Empty;
            }
        }
    
        #endregion
    
        #region Constructor
    
        static DataGridHeader()
        {
            PropertyPathProperty = DependencyProperty.RegisterAttached("PropertyPath",
                                                                       typeof(string),
                                                                       typeof(DataGridHeader),
                                                                       new UIPropertyMetadata(String.Empty, IsSetHeader));
    
            StringFormatProperty = DependencyProperty.RegisterAttached("StringFormat",
                                                                       typeof(string),
                                                                       typeof(DataGridHeader),
                                                                       new UIPropertyMetadata(String.Empty));  
        }
    
        #endregion
    
        #region IsSetHeader PropertyChanged Handler
    
        private static void IsSetHeader(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var textColumn = sender as DataGridTextColumn;
            var templateColumn = sender as DataGridTemplateColumn;
            string path = e.NewValue as string;
    
            if ((textColumn == null) & (templateColumn == null)) 
            {
                return;
            }
    
            if (String.IsNullOrEmpty(path) == false)
            {
                currentStringFormat = ReturnStringFormat(textColumn, templateColumn);
                dataTemplate = CreateDynamicDataTemplate(path, currentStringFormat);
    
                if (dataTemplate != null)
                {
                    if (textColumn != null)
                        textColumn.HeaderTemplate = dataTemplate;
    
                    if (templateColumn != null)
                        templateColumn.HeaderTemplate = dataTemplate;
                }
            }
        }
    
        #endregion
    
        #region ReturnStringFormat Helper
    
        private static string ReturnStringFormat(DependencyObject depObject1, DependencyObject depObject2) 
        {
            textColumnStringFormat = GetStringFormat(depObject1) as string;
            templateColumnStringFormat = GetStringFormat(depObject2) as string;
    
            if (String.IsNullOrEmpty(textColumnStringFormat) == false)
            {
                return textColumnStringFormat;
            }
    
            if (String.IsNullOrEmpty(templateColumnStringFormat) == false)
            {
                return templateColumnStringFormat;
            }
    
            return "null";
        }
    
        #endregion
    
        #region CreateDynamicDataTemplate Helper
    
        private static DataTemplate CreateDynamicDataTemplate(string propertyPath, string stringFormat)
        {
            var pc = new ParserContext();
            MemoryStream sr = null;
    
            string xaml = GetXamlString(propertyPath, stringFormat);            
            sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
    
            pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
            pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
    
            return XamlReader.Load(sr, pc) as DataTemplate;
        }
    
        #endregion
    
        #region GetXamlString Helper
    
        private static string GetXamlString(string propertyPath, string stringFormat)
        {
            #region Original PropertyPath for TextBlock
    
            // {Binding Path=DataContext.YourProperty, RelativeSource={RelativeSource AncestorType={x:Type DataGridCellsPanel}}}"
            // Thanks to Bruno (https://stackoverflow.com/users/248118/bruno) for this trick
    
            #endregion
    
            var sb = new StringBuilder();
    
            sb.Append("");
    
            return sb.ToString();
        }
    
        #endregion
    }
    

    XAML

    
    
        
            
        
    
        
            
    
                
                    
    
                        
                            
                        
                    
    
                    
    
                        
                            
                        
                    
    
                    
    
                        
                            
                        
                    
    
                    
    
                        
                            
                        
                    
                
            
    
            

    Code-behind

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }        
    
        private void ChangeHeader_Click(object sender, RoutedEventArgs e)
        {
            TestData data = this.DataContext as TestData;
    
            data.TestStringFormatValue = "777";
            data.TestUsualHeaderValue = "DynamicUsualHeader";
            data.TestTemplateColumnValue = "DynamicTemplateColumn";
        }
    }
    
    public class TestData : NotificationObject
    {
        #region TestStringFormatValue
    
        private string _testStringFormatValue = "1";
    
        public string TestStringFormatValue
        {
            get
            {
                return _testStringFormatValue;
            }
    
            set
            {
                _testStringFormatValue = value;
                NotifyPropertyChanged("TestStringFormatValue");
            }
        }
    
        #endregion
    
        #region TestStaticStringFormatValue
    
        public static string TestStaticStringFormatValue = "Static StringFormat: {0}$";
    
        #endregion
    
        #region TestUsualHeaderValue
    
        private string _testUsualHeaderValue = "UsualHeader";
    
        public string TestUsualHeaderValue
        {
            get
            {
                return _testUsualHeaderValue;
            }
    
            set
            {
                _testUsualHeaderValue = value;
                NotifyPropertyChanged("TestUsualHeaderValue");
            }
        }
    
        #endregion
    
        #region TestTemplateColumnValue
    
        private string _testTemplateColumnValue = "TemplateColumn";
    
        public string TestTemplateColumnValue
        {
            get
            {
                return _testTemplateColumnValue;
            }
    
            set
            {
                _testTemplateColumnValue = value;
                NotifyPropertyChanged("TestTemplateColumnValue");
            }
        }
    
        #endregion
    }
    

提交回复
热议问题