最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to bind to different abstract class implementations with additional properties without binding failure in WPF - Stack Overfl

programmeradmin2浏览0评论

I implemented the classes UtilityElectricityConsumptionModel and UtilityNaturalGasConsumptionModel from the abstract class UtilityConsumptionModel. The UtilityNaturalGasConsumptionModel just has the extra ConversionCoefficient property added.

I bind to ConversionCoefficient in a DataGrid via the DataGridTemplateColumn and Trigger. I don't get any binding failure initially, but I get binding failures since I begin scrolling the DataGrid. Is there a way to avoid these binding failures?

<!--  Conversion Coefficient  -->
<DataGridTemplateColumn Width="100" Header="Conversion Coefficient">

    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>

            <TextBlock>

                <TextBlock.Style>

                    <Style TargetType="{x:Type TextBlock}">

                        <Setter Property="HorizontalAlignment" Value="Stretch" />
                        <Setter Property="Background" Value="{StaticResource PrimarySolidColorBrush}" />

                        <Style.Triggers>

                            <DataTrigger Binding="{Binding Source}" Value="{x:Static models:UtilitySource.NaturalGas}">

                                <Setter Property="Padding" Value="2,0,2,0" />
                                <Setter Property="HorizontalAlignment" Value="Right" />
                                <Setter Property="Background" Value="Transparent" />
                                <Setter Property="Text" Value="{Binding ConversionCoefficient, StringFormat='0.00'}" />

                            </DataTrigger>

                        </Style.Triggers>

                    </Style>

                </TextBlock.Style>

            </TextBlock>

        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>

</DataGridTemplateColumn>

I implemented the classes UtilityElectricityConsumptionModel and UtilityNaturalGasConsumptionModel from the abstract class UtilityConsumptionModel. The UtilityNaturalGasConsumptionModel just has the extra ConversionCoefficient property added.

I bind to ConversionCoefficient in a DataGrid via the DataGridTemplateColumn and Trigger. I don't get any binding failure initially, but I get binding failures since I begin scrolling the DataGrid. Is there a way to avoid these binding failures?

<!--  Conversion Coefficient  -->
<DataGridTemplateColumn Width="100" Header="Conversion Coefficient">

    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>

            <TextBlock>

                <TextBlock.Style>

                    <Style TargetType="{x:Type TextBlock}">

                        <Setter Property="HorizontalAlignment" Value="Stretch" />
                        <Setter Property="Background" Value="{StaticResource PrimarySolidColorBrush}" />

                        <Style.Triggers>

                            <DataTrigger Binding="{Binding Source}" Value="{x:Static models:UtilitySource.NaturalGas}">

                                <Setter Property="Padding" Value="2,0,2,0" />
                                <Setter Property="HorizontalAlignment" Value="Right" />
                                <Setter Property="Background" Value="Transparent" />
                                <Setter Property="Text" Value="{Binding ConversionCoefficient, StringFormat='0.00'}" />

                            </DataTrigger>

                        </Style.Triggers>

                    </Style>

                </TextBlock.Style>

            </TextBlock>

        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>

</DataGridTemplateColumn>
Share Improve this question asked Jan 19 at 7:32 Ludovic WagnerLudovic Wagner 891 silver badge9 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

After testing the "quick mock" in my other post, it occurred to me that there is an improved approach that might answer your question:

Is there a way to avoid these binding failures?

Use a DataTemplateSelector in place of the DataTrigger stategy.


public class UtilityConsumptionTemplateSelector : DataTemplateSelector
{
    public DataTemplate? ElectricityTemplate { get; set; }
    public DataTemplate? NaturalGasTemplate { get; set; }

    public override DataTemplate? SelectTemplate(object item, DependencyObject container)
    {
        if (item is UtilityElectricityConsumptionModel)
        {
            return ElectricityTemplate;
        }
        if (item is UtilityNaturalGasConsumptionModel)
        {
            return NaturalGasTemplate;
        }
        return base.SelectTemplate(item, container);
    }
}

<Window x:Class="abstract_class_binding_issue.MainWindow"
        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"
        xmlns:local="clr-namespace:abstract_class_binding_issue"
        xmlns:models="clr-namespace:abstract_class_binding_issue.Models"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="400">
    <Window.Resources>
        <SolidColorBrush x:Key="PrimarySolidColorBrush" Color="LightGray"/>
        <DataTemplate x:Key="ElectricityTemplate">
            <TextBlock Text="Electricity" Background="{StaticResource PrimarySolidColorBrush}" HorizontalAlignment="Center"/>
        </DataTemplate>
        <DataTemplate x:Key="NaturalGasTemplate">
            <TextBlock Text="{Binding ConversionCoefficient, StringFormat='0.00'}"
                   Background="Transparent" HorizontalAlignment="Right" Padding="2,0,2,0"/>
        </DataTemplate>
        <local:UtilityConsumptionTemplateSelector 
            x:Key="UtilityTemplateSelector"
            ElectricityTemplate="{StaticResource ElectricityTemplate}"
            NaturalGasTemplate="{StaticResource NaturalGasTemplate}" />
        </Window.Resources>
        <Window.DataContext>
        <models:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <DataGrid ItemsSource="{Binding UtilityConsumptions}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTemplateColumn Width="100" Header="Conversion Coefficient"
                                    CellTemplateSelector="{StaticResource UtilityTemplateSelector}" />
                <DataGridTextColumn Header="Utility Type" Binding="{Binding Source}" Width="200"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

I tried to do a quick mock to reproduce your issue, and couldn't. Where did I go wrong? What did we do differently? It scrolls just fine on my end.

<Window x:Class="abstract_class_binding_issue.MainWindow"
        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"
        xmlns:local="clr-namespace:abstract_class_binding_issue"
        xmlns:models="clr-namespace:abstract_class_binding_issue.Models"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="400">
    <Window.Resources>
        <SolidColorBrush x:Key="PrimarySolidColorBrush" Color="LightGray"/>
    </Window.Resources>
    <Window.DataContext>
        <models:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <DataGrid ItemsSource="{Binding UtilityConsumptions}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTemplateColumn Width="100" Header="Conversion Coefficient">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock>
                                <TextBlock.Style>
                                    <Style TargetType="{x:Type TextBlock}">
                                        <Setter Property="HorizontalAlignment" Value="Stretch" />
                                        <Setter Property="Background" Value="{StaticResource PrimarySolidColorBrush}" />
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Source}" Value="{x:Static models:UtilitySource.NaturalGas}">
                                                <Setter Property="Padding" Value="2,0,2,0" />
                                                <Setter Property="HorizontalAlignment" Value="Right" />
                                                <Setter Property="Background" Value="Transparent" />
                                                <Setter Property="Text" Value="{Binding ConversionCoefficient, StringFormat='0.00'}" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="Utility Type" Binding="{Binding Source}" Width="200"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>
namespace abstract_class_binding_issue
{
    public partial class MainWindow : Window
    {
        public MainWindow() =>  InitializeComponent();
    }
    namespace Models
    {
        class MainViewModel
        {
            public IList UtilityConsumptions { get; } = new ObservableCollection<UtilityConsumptionModel>();

            public MainViewModel()
            {
                object animation = UtilitySource.Electricity;
                var s = $"{animation.GetType().Name}.{animation}";


                for (int i = 0; i < 100; i++) // Add enough items for scrolling
                {
                    if (i % 2 == 0)
                    {
                        UtilityConsumptions.Add(new UtilityNaturalGasConsumptionModel { ConversionCoefficient = i * 1.5 });
                    }
                    else
                    {
                        UtilityConsumptions.Add(new UtilityElectricityConsumptionModel());
                    }
                }
            }
        }
        abstract class UtilityConsumptionModel : INotifyPropertyChanged
        {
            protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            public event PropertyChangedEventHandler? PropertyChanged;
        }
        class UtilityElectricityConsumptionModel : UtilityConsumptionModel
        {
            public UtilitySource Source => UtilitySource.Electricity;
        }
        class UtilityNaturalGasConsumptionModel : UtilityConsumptionModel
        {
            public UtilitySource Source => UtilitySource.NaturalGas;
            public double ConversionCoefficient
            {
                get => _conversionCoefficient;
                set
                {
                    if (!Equals(_conversionCoefficient, value))
                    {
                        _conversionCoefficient = value;
                        OnPropertyChanged();
                    }
                }
            }
            double _conversionCoefficient = default;
        }
        enum UtilitySource
        {
            Electricity,
            NaturalGas
        }
    }
}

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论