在动态 DataGrid 中启用 TextWrap

发布于 2025-01-04 01:43:39 字数 2622 浏览 0 评论 0原文

我正在动态生成一个 DataGrid 并将其添加到我的 WPF 应用程序上的 StackPanel 中。

由于是动态生成的,因此 XAML 端没有相同的标记,我需要以编程方式管理绑定和所有属性。

如果文本很长,我希望我的 DataGrid 将单元格中的值换行到下一行。我知道我需要将 DataGridCell 替换为 TextBlock 并在其上设置 TextWrap 属性。我发现的所有例子都表明了这些内容本身。但是,我找不到一种方法可以在没有 XAML 的情况下完全从代码隐藏来完成此操作。

到目前为止,我已经尝试过以下代码,但它不起作用。

DataGrid dg = new DataGrid();   

dg.ItemsSource = ((DataSet)data).Tables[0].DefaultView;
dg.DataContext = ((DataSet)data).Tables[0].DefaultView; 

DataTemplate ct = new DataTemplate(typeof(DataGridCell));
FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
ct.VisualTree = tb;

dg.ItemTemplate = ct;
dg.ColumnWidth = 300;

你能指出我正确的方向吗?

[更新]:解决方案

通过进一步研究,我找到了问题的解决方案。对于自动生成的列,我们需要捕获 AutoGenerateColumn 事件,并用具有 TextBlock< 的 DataGridTemplateColumn 替换默认的 DataGridTextColumn /代码> 在其中。然后我们可以设置“TextWrappingProperty”来换行文本。

以下是更新后的代码:

DataGrid dg = new DataGrid();   

dg.ItemsSource = ((DataSet)data).Tables[0].DefaultView;
dg.DataContext = ((DataSet)data).Tables[0].DefaultView; 

DataTemplate ct = new DataTemplate(typeof(DataGridCell));
FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
ct.VisualTree = tb;

dg.AutoGeneratingColumn += new EventHandler<DataGridAutoGeneratingColumnEventArgs>(dg_AutoGeneratingColumn);

dg.MaxColumnWidth = 300;

然后是事件处理程序下的代码:

 private void dg_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
        //cancel the auto generated column
        e.Cancel = true;

        //Get the existing column
        DataGridTextColumn dgTextC = (DataGridTextColumn)e.Column;

        //Create a new template column 
        DataGridTemplateColumn dgtc = new DataGridTemplateColumn();

        DataTemplate dataTemplate = new DataTemplate(typeof(DataGridCell));

        FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
        tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
        dataTemplate.VisualTree = tb;

        dgtc.Header = dgTextC.Header;
        dgtc.CellTemplate = dataTemplate;

        tb.SetBinding(TextBlock.TextProperty, dgTextC.Binding);

        //add column back to data grid
        DataGrid dg = sender as DataGrid;
        dg.Columns.Add(dgtc);
    }

I am generating a DataGrid dynamically and adding it to a StackPanel on my WPF application.

As the is dynamically generated, there is no mark up on XAML side for the same and I need to manage the binding and all properties programatically.

I want my DataGrid to have the values in the cell wrapped to the next line if the text is lengthy. I understand that I need to replace the DataGridCell with TextBlock and set the TextWrap property on it. All the examples that I have found suggest something on those lines itself. However, I couldn't find a way to do it completely from code behind, without XAML.

So far, I have tried to the following code, but it doesn't work.

DataGrid dg = new DataGrid();   

dg.ItemsSource = ((DataSet)data).Tables[0].DefaultView;
dg.DataContext = ((DataSet)data).Tables[0].DefaultView; 

DataTemplate ct = new DataTemplate(typeof(DataGridCell));
FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
ct.VisualTree = tb;

dg.ItemTemplate = ct;
dg.ColumnWidth = 300;

Can you please point me to the right direction here?

[Update]: Solution

On further researching I was able to get a solution to my issue. For Auto generated columns, we need to capture the AutoGeneratingColumn event and replace the default DataGridTextColumn by a DataGridTemplateColumn which would have a TextBlock in it. And we can then set the `TextWrappingProperty' to get the text wrapped.

Following is the updated code:

DataGrid dg = new DataGrid();   

dg.ItemsSource = ((DataSet)data).Tables[0].DefaultView;
dg.DataContext = ((DataSet)data).Tables[0].DefaultView; 

DataTemplate ct = new DataTemplate(typeof(DataGridCell));
FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
ct.VisualTree = tb;

dg.AutoGeneratingColumn += new EventHandler<DataGridAutoGeneratingColumnEventArgs>(dg_AutoGeneratingColumn);

dg.MaxColumnWidth = 300;

and then the Code under the Event Handler:

 private void dg_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
        //cancel the auto generated column
        e.Cancel = true;

        //Get the existing column
        DataGridTextColumn dgTextC = (DataGridTextColumn)e.Column;

        //Create a new template column 
        DataGridTemplateColumn dgtc = new DataGridTemplateColumn();

        DataTemplate dataTemplate = new DataTemplate(typeof(DataGridCell));

        FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
        tb.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
        dataTemplate.VisualTree = tb;

        dgtc.Header = dgTextC.Header;
        dgtc.CellTemplate = dataTemplate;

        tb.SetBinding(TextBlock.TextProperty, dgTextC.Binding);

        //add column back to data grid
        DataGrid dg = sender as DataGrid;
        dg.Columns.Add(dgtc);
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

人生百味 2025-01-11 01:43:39

另一种方法是使用这样的行为。

  public class DataGridWrapTextBehaviour : Behavior<DataGrid>
  {
     private DataGrid DataGrid
     {
        get { return AssociatedObject as DataGrid; }
     }

     private Style ElementStyle { get; set; }
     private Style EditingElementStyle { get; set; }

     protected override void OnAttached()
     {
        base.OnAttached();

        this.ElementStyle = new Style( typeof( TextBlock ) );
        this.ElementStyle.Setters.Add( new Setter( TextBlock.TextWrappingProperty, TextWrapping.Wrap ) );

        this.EditingElementStyle = new Style( typeof( TextBox ) );
        this.EditingElementStyle.Setters.Add( new Setter( TextBox.TextWrappingProperty, TextWrapping.Wrap ) );

        this.DataGrid.Columns.CollectionChanged += Columns_CollectionChanged;
     }

     protected override void OnDetaching()
     {
        this.DataGrid.Columns.CollectionChanged -= Columns_CollectionChanged;
        base.OnDetaching();
     }

     private void Columns_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
     {
        foreach ( var column in this.DataGrid.Columns.OfType<DataGridTextColumn>() )
        {
           column.ElementStyle = this.ElementStyle;
           column.EditingElementStyle = this.EditingElementStyle;
        }
     }
  }

然后,您可以将行为拖放到 Expression Blend 中的 DataGrid 上。

An alternate approach is to use a behaviour like this.

  public class DataGridWrapTextBehaviour : Behavior<DataGrid>
  {
     private DataGrid DataGrid
     {
        get { return AssociatedObject as DataGrid; }
     }

     private Style ElementStyle { get; set; }
     private Style EditingElementStyle { get; set; }

     protected override void OnAttached()
     {
        base.OnAttached();

        this.ElementStyle = new Style( typeof( TextBlock ) );
        this.ElementStyle.Setters.Add( new Setter( TextBlock.TextWrappingProperty, TextWrapping.Wrap ) );

        this.EditingElementStyle = new Style( typeof( TextBox ) );
        this.EditingElementStyle.Setters.Add( new Setter( TextBox.TextWrappingProperty, TextWrapping.Wrap ) );

        this.DataGrid.Columns.CollectionChanged += Columns_CollectionChanged;
     }

     protected override void OnDetaching()
     {
        this.DataGrid.Columns.CollectionChanged -= Columns_CollectionChanged;
        base.OnDetaching();
     }

     private void Columns_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
     {
        foreach ( var column in this.DataGrid.Columns.OfType<DataGridTextColumn>() )
        {
           column.ElementStyle = this.ElementStyle;
           column.EditingElementStyle = this.EditingElementStyle;
        }
     }
  }

You can then drap and drop the behaviour onto the DataGrid in Expression Blend.

蓝海似她心 2025-01-11 01:43:39

我必须说的第一件事是您使用数据网格的方式是错误的。

 dg.ItemTemplate = ct; 

是一个错误的代码!

WPF DataGrid 不像其他 ItemsControls 那样支持 ItemTemplate 属性。您必须在 DataGrid 中提供列模板才能使其正常工作。

DataGridBoundColumn \ DataGridTextColumn 列提供给 DataGrid 时,您可以像这样设置它们的 ElementStyle 属性...

        <toolkit:DataGrid
                 AutoGenerateColumns="False">                
            <toolkit:DataGrid.Columns>
                <toolkit:DataGridTextColumn 
                         Binding="{Binding SomeProperty}">
                    <toolkit:DataGridTextColumn.ElementStyle>
                        <Style TargetType="{x:Type TextBlock}">
                            <Setter Property="TextWrapping"
                                    Value="Wrap"/>
                        </Style>
                    </toolkit:DataGridTextColumn.ElementStyle>
                </toolkit:DataGridTextColumn>
            </toolkit:DataGrid.Columns>
        </toolkit:DataGrid>     

...其中 toolkit 是 .Net 3.5 或更早版本中 WPF 工具包的命名空间。在 .Net 4.0 中,它是标准 System.Windows.Controls 命名空间的一部分。

但在您的情况下,完全相同的解决方案有点复杂,因为默认情况下您的网格将 AutoGenerateColumns 设置为 true,自动生成 DataGrid.Columns。因此您没有范围来设置此 ElementStyle 属性。

因此,我们必须采用 XML 和代码隐藏方法...

XAML:

       <toolkit:DataGrid x:Name="dg">
            <toolkit:DataGrid.Resources>
                <Style TargetType="{x:Type TextBlock}"
                       x:Key="WrappedTextBlockStyle">
                    <Setter Property="TextWrapping" Value="Wrap"/>
                </Style>
            </toolkit:DataGrid.Resources>                
        </toolkit:DataGrid>

代码隐藏:

    dg.ItemsSource = ((DataSet)data).Table[0].DefaultView;
    dg.Columns.CollectionChanged
       += new NotifyCollectionChangedEventHandler(
              Columns_CollectionChanged);

    void Columns_CollectionChanged(
         object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems.Count > 0)
        {
            foreach(DataGridColumn col in e.NewItems)
            {
                if (col is DataGridTextColumn)
                {
                    ((DataGridTextColumn) col).ElementStyle
                         = dg.Resources["WrappedTextBlockStyle"] as Style;
                }
            }
        }
    }

希望这会有所帮助...

注意: 一句话请注意,换行文本块会使 WPF dataGrid 的性能变慢。

First thing I must say is that you are using the datagrid in a wrong manner.

 dg.ItemTemplate = ct; 

is a wrong code!

WPF DataGrid does not entertain ItemTemplate property as other ItemsControls do. You would have to supply column templates in DataGrid for it to work correctly.

When DataGridBoundColumn \ DataGridTextColumn columns are supplied to the DataGrid , you could set their ElementStyle property like this...

        <toolkit:DataGrid
                 AutoGenerateColumns="False">                
            <toolkit:DataGrid.Columns>
                <toolkit:DataGridTextColumn 
                         Binding="{Binding SomeProperty}">
                    <toolkit:DataGridTextColumn.ElementStyle>
                        <Style TargetType="{x:Type TextBlock}">
                            <Setter Property="TextWrapping"
                                    Value="Wrap"/>
                        </Style>
                    </toolkit:DataGridTextColumn.ElementStyle>
                </toolkit:DataGridTextColumn>
            </toolkit:DataGrid.Columns>
        </toolkit:DataGrid>     

... where toolkit is namespace of the WPF toolkit in version .Net 3.5 or prior. In .Net 4.0, its part of the standard System.Windows.Controls namespace.

But that exact same solution is a little complicated in your case is because your grid has AutoGenerateColumns as true by default that generates the DataGrid.Columns automatically. So you have no scope to set this ElementStyle property.

So we have to take the XML and code behind approach ...

XAML:

       <toolkit:DataGrid x:Name="dg">
            <toolkit:DataGrid.Resources>
                <Style TargetType="{x:Type TextBlock}"
                       x:Key="WrappedTextBlockStyle">
                    <Setter Property="TextWrapping" Value="Wrap"/>
                </Style>
            </toolkit:DataGrid.Resources>                
        </toolkit:DataGrid>

Code Behind:

    dg.ItemsSource = ((DataSet)data).Table[0].DefaultView;
    dg.Columns.CollectionChanged
       += new NotifyCollectionChangedEventHandler(
              Columns_CollectionChanged);

    void Columns_CollectionChanged(
         object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems.Count > 0)
        {
            foreach(DataGridColumn col in e.NewItems)
            {
                if (col is DataGridTextColumn)
                {
                    ((DataGridTextColumn) col).ElementStyle
                         = dg.Resources["WrappedTextBlockStyle"] as Style;
                }
            }
        }
    }

Hope this helps...

Note: A word of caution that wrapping textblocks make the performance of WPF dataGrid slower.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文