WPF DataGrid 单元格字符串格式作为样式或模板

发布于 2024-11-13 23:56:23 字数 3167 浏览 3 评论 0原文

我尝试了多种方法来分解 WPF DataGrid 中单元格的格式(.NET 4 提供的一种):

  • 数据转换器、
  • 样式中的“StringFormat”绑定属性、
  • 数据模板中的“StringFormat”绑定属性。

我将描述我的尝试,因为它可能对其他人有帮助,并且我希望有人能给我建议以改进这些解决方案。请注意,我对 WPF 相当陌生...

预期的行为是将单元格字符串格式化为特定的内容,例如“1,234,567”以进行显示,但在编辑单元格时应将其格式化为“1234567”(其默认格式)。当我尝试使用数据转换器时,我没有找到在编辑时使用默认格式的方法,因此我将精力集中在样式和模板上。

对于样式,DataGridTextColumn 的定义如下所示:

<DataGridTextColumn Header="Price (Style)" SortMemberPath="BILL_PRICE">
    <DataGridTextColumn.ElementStyle>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Text" Value="{Binding Path=BILL_PRICE, StringFormat={}{0:N0}}"/>
        </Style>
    </DataGridTextColumn.ElementStyle>
    <DataGridTextColumn.EditingElementStyle>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Text" Value="{Binding Path=BILL_PRICE}"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="BorderThickness" Value="0"/>
        </Style>
    </DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>

行为正是预期的。但是,由于绑定的原因,我无法分解这种样式并多次使用它。为了解决分解问题,我使用了 DataGridTemplateColumn 和数据模板。这是我的 DataGridTemplateColumn 定义:

<DataGridTemplateColumn Header="Price (Template)" SortMemberPath="BILL_PRICE">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding BILL_PRICE}" Template="{StaticResource CurrencyCellControlTemplate}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding BILL_PRICE, Mode=TwoWay}" Template="{StaticResource CurrencyCellEditingControlTemplate}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

和 ControlTemplate 定义:

<ControlTemplate x:Key="CurrencyCellControlTemplate" TargetType="ContentControl">
    <TextBlock Margin="2,0,2,0" Padding="0" TextAlignment="Right">
        <TextBlock.Text>
            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Content" StringFormat="{}{0:N0}"/>
        </TextBlock.Text>
    </TextBlock>
</ControlTemplate>

<ControlTemplate x:Key="CurrencyCellEditingControlTemplate" TargetType="ContentControl">
    <TextBox Padding="0" BorderThickness="0" TextAlignment="Right">
        <TextBox.Text>
            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Content"/>
        </TextBox.Text>
    </TextBox>
</ControlTemplate>

使用数据模板解决了分解 DataGrid 单元格格式的最初问题,但使用控件模板会带来人体工程学和视觉问题。例如,由控件模板引起的双标签导航(在许多其他地方讨论过),以及编辑文本框的外观(我尝试使用边框厚度、填充和其他属性设置来修复它。)

相关的具体问题这个问题是:

  • 可以使用数据转换器来格式化要显示的字符串并使用默认格式进行编辑吗?
  • 是否可以分解 DataGridTextColumn 样式,同时仍然能够指定绑定源?
  • 有没有一种方法可以使用 DataGridTemplateColumn,但只是使其看起来像 DataGridTextColumn?

I tried several ways to factor out the format of a cell in a WPF DataGrid (the one provided with .NET 4):

  • a data converter,
  • the "StringFormat" binding property within a style,
  • the "StringFormat" binding property within a data template.

I will describe my attempts as it might help someone else, and I hope someone can give me advice to improve on these solutions. Note that I am fairly new to WPF...

The expected behaviour is that the cell string is formatted to something specific like "1,234,567" for display, but it should be formatted as "1234567" (its default formatting) when editing the cell. When I tried to use a data converter, I did not find a way to use the default formatting when editing, so I focused my energy on styles and templates.

With a style, the definition of the DataGridTextColumn goes like this:

<DataGridTextColumn Header="Price (Style)" SortMemberPath="BILL_PRICE">
    <DataGridTextColumn.ElementStyle>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Text" Value="{Binding Path=BILL_PRICE, StringFormat={}{0:N0}}"/>
        </Style>
    </DataGridTextColumn.ElementStyle>
    <DataGridTextColumn.EditingElementStyle>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Text" Value="{Binding Path=BILL_PRICE}"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="BorderThickness" Value="0"/>
        </Style>
    </DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>

The behaviour is exactly what is expected. However, I cannot factor out this style and use it several times, because of the binding. To solve the factoring problem, I used a DataGridTemplateColumn and data templates. Here is my DataGridTemplateColumn definition:

<DataGridTemplateColumn Header="Price (Template)" SortMemberPath="BILL_PRICE">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding BILL_PRICE}" Template="{StaticResource CurrencyCellControlTemplate}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding BILL_PRICE, Mode=TwoWay}" Template="{StaticResource CurrencyCellEditingControlTemplate}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

And the ControlTemplate definition:

<ControlTemplate x:Key="CurrencyCellControlTemplate" TargetType="ContentControl">
    <TextBlock Margin="2,0,2,0" Padding="0" TextAlignment="Right">
        <TextBlock.Text>
            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Content" StringFormat="{}{0:N0}"/>
        </TextBlock.Text>
    </TextBlock>
</ControlTemplate>

<ControlTemplate x:Key="CurrencyCellEditingControlTemplate" TargetType="ContentControl">
    <TextBox Padding="0" BorderThickness="0" TextAlignment="Right">
        <TextBox.Text>
            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Content"/>
        </TextBox.Text>
    </TextBox>
</ControlTemplate>

Using data templates solves the initial problem of factoring out DataGrid cell formatting, but using a control template brings up ergonomic and visual problems. For instance, the double-tab navigation caused by the control template (discussed in many other places), and the look of the editing text box (which I try to fix with border thickness, padding and the other property settings.)

Specific questions related to this issue are :

  • Can a data converter be used to format the string for display and use the default formatting for edition?
  • Is it possible to factor out the DataGridTextColumn style while still being able to specify the binding source?
  • Is there a way to use a DataGridTemplateColumn but simply make it look and feel like a DataGridTextColumn?

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

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

发布评论

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

评论(1

一口甜 2024-11-20 23:56:23

创建您自己的自定义 DataGridTextColumn 并创建绑定以分配给 Element 和 EditingElement(其中之一带有转换器,其中之一没有)。

转换器将字符串格式化为带有逗号的小数。

 public class MyDataGridTextColumn : DataGridTextColumn
{
    Binding formattedBinding;
    Binding unformattedBinding;
    FormatConverter formatConverter = new FormatConverter();

    protected override FrameworkElement
    GenerateElement(DataGridCell cell, object dataItem)
    {
       var element = base.GenerateElement(cell, dataItem) as TextBlock;
       element.SetBinding(TextBlock.TextProperty, GetFormattedTextBinding());
        return element;
    }

    protected override FrameworkElement
    GenerateEditingElement (DataGridCell cell, object dataItem)
    {
        var element = base.GenerateEditingElement(cell, dataItem) as TextBox;
        element.SetBinding(TextBox.TextProperty, GetTextBinding());
        return element;
    }

    Binding
    GetTextBinding()
    {

            var binding = (Binding)Binding;
            if (binding == null) return new Binding();
            unformattedBinding = new Binding
            {
                Path = binding.Path,
                Mode=BindingMode.TwoWay
            };

        return unformattedBinding;
    }

    Binding
    GetFormattedTextBinding()
    {
         var binding = (Binding)Binding;
            if (binding == null) return new Binding();
            formattedBinding = new Binding
            {
                Path = binding.Path,
                Converter = Formatter,
            };

        return formattedBinding;
    }

    public FormatConverter
    Formatter
    {
        get { return formatConverter; }
        set { formatConverter = value; }
    }
}
public class FormatConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string textvalue = value as string;
        if (!string.IsNullOrEmpty(textvalue))
        {
            decimal decimalvalue = decimal.Parse(textvalue);
            var test = String.Format("{0:0,0.00}", decimalvalue);
            return test;
        }
        else
            return "";

    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new InvalidOperationException("FormatConverter can only be used OneWay.");
    }
}

我不明白你发布的格式化字符串是如何工作的,所以我做了一个简单的,如果你想要更多的小数位,只需在转换器中对其进行排序即可。

现在您需要做的就是在 xaml 中放置对命名空间的引用,然后在数据网格中创建一个列。

那么一切都会好起来

u_u

Make your own custom DataGridTextColumn and creates bindings to assign to the Element and the EditingElement (one of them with a converter one of them without).

The converter formats the string to look like a decimal with the commas.

 public class MyDataGridTextColumn : DataGridTextColumn
{
    Binding formattedBinding;
    Binding unformattedBinding;
    FormatConverter formatConverter = new FormatConverter();

    protected override FrameworkElement
    GenerateElement(DataGridCell cell, object dataItem)
    {
       var element = base.GenerateElement(cell, dataItem) as TextBlock;
       element.SetBinding(TextBlock.TextProperty, GetFormattedTextBinding());
        return element;
    }

    protected override FrameworkElement
    GenerateEditingElement (DataGridCell cell, object dataItem)
    {
        var element = base.GenerateEditingElement(cell, dataItem) as TextBox;
        element.SetBinding(TextBox.TextProperty, GetTextBinding());
        return element;
    }

    Binding
    GetTextBinding()
    {

            var binding = (Binding)Binding;
            if (binding == null) return new Binding();
            unformattedBinding = new Binding
            {
                Path = binding.Path,
                Mode=BindingMode.TwoWay
            };

        return unformattedBinding;
    }

    Binding
    GetFormattedTextBinding()
    {
         var binding = (Binding)Binding;
            if (binding == null) return new Binding();
            formattedBinding = new Binding
            {
                Path = binding.Path,
                Converter = Formatter,
            };

        return formattedBinding;
    }

    public FormatConverter
    Formatter
    {
        get { return formatConverter; }
        set { formatConverter = value; }
    }
}
public class FormatConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string textvalue = value as string;
        if (!string.IsNullOrEmpty(textvalue))
        {
            decimal decimalvalue = decimal.Parse(textvalue);
            var test = String.Format("{0:0,0.00}", decimalvalue);
            return test;
        }
        else
            return "";

    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new InvalidOperationException("FormatConverter can only be used OneWay.");
    }
}

I didn't understand how the formatting string you posted works so I made a simple one, if you want more decimal places just sort it out in the converter.

All you need to do now is place a reference to the namespace in your xaml and then create a column in the datagrid.

Then all should be well

u_u

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