dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.08k stars 1.17k forks source link

How to manually trigger the converter method for property of ComboBox in WPF #9242

Open vsfeedback opened 5 months ago

vsfeedback commented 5 months ago

This issue has been moved from a ticket on Developer Community.


[severity:It bothers me. A fix would be nice]

public enum AlarmLevel
{
    [Description("正常")]
    Normal = 0,

[Description("警报")]
    Warring = 1,

[Description("故障")]
    Error = 2,
}

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        InitList();
    }

public List<AlarmLevel> AlarmLevelList
    {
        get
        {
            return _alarmLevelList;
        }
        set
        {
            _alarmLevelList = value;
            DoPropertyChanged(AlarmLevelListPropertyName);
        }
    }

private List<AlarmLevel> _alarmLevelList;
    public const string AlarmLevelListPropertyName = "AlarmLevelList";

public AlarmLevel SelectedAlarmLevel
    {
        get { return _selectedAlarmLevel; }
        set
        {
            if (_selectedAlarmLevel != value)
            {
                _selectedAlarmLevel = value;
                DoPropertyChanged(SelectedAlarmLevelPropertyName);
            }
        }
    }
    private AlarmLevel _selectedAlarmLevel;
    public const string SelectedAlarmLevelPropertyName = "SelectedAlarmLevel";

public void InitList()
    {
        AlarmLevelList = new List<AlarmLevel>();
        AlarmLevelList.Add(AlarmLevel.Normal);
        AlarmLevelList.Add(AlarmLevel.Warring);
        AlarmLevelList.Add(AlarmLevel.Error);
    }

private void Button_Click(object sender, RoutedEventArgs e)
    {
        DoPropertyChanged(AlarmLevelListPropertyName);
        DoPropertyChanged(SelectedAlarmLevelPropertyName);
    }

public event PropertyChangedEventHandler PropertyChanged;

protected void DoPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class EnumDescriptionConverter : IValueConverter
{
    object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value as string != "")
        {
            Enum myEnum = (Enum)value;
            string description = GetEnumDescription(myEnum);
            return description;
        }

return "";
    }

object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return string. Empty;
    }

private string GetEnumDescription(Enum enumObj)
    {
        FieldInfo fieldInfo = enumObj.GetType(). GetField(enumObj.ToString());

object[] attribArray = fieldInfo.GetCustomAttributes(false);

if (attribArray.Length == 0)
        {
            return enumObj.ToString();
        }
        else
        {`
            DescriptionAttribute attrib = attribArray[0] as DescriptionAttribute;
            return attrib. Description;
        }
    }
}

<ComboBox Width="200" Height="50" Margin="100"
          HorizontalAlignment="Center"
          VerticalAlignment="Center"
          ItemsSource="{Binding AlarmLevelList}"
          SelectedItem="{Binding SelectedAlarmLevel}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource enumDescriptionConverter}}" />
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

<Button Content="OK" Width="100" Height="30" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click"/>

There are two controls on UI, one is ComboBox, and the other is button, when user press Button, how to trigger converter enumDescriptionConverter of ComboBox control manually in Button_Click event?

Only after application is launched for first time, the converter enumDescriptionConverter can be executed. So I have tried writing following code within Button Click method, and it works as expected (the converter was triggered four times, three times for ItemSource, and one time for SelectedItem). But I think it is not a good style of writing code. Could anyone give good suggestion than this one? Thanks in advance!

private void Button_Click(object sender, RoutedEventArgs e)
{
        List<FlushingConditions> tempList = AlarmLevelList ;
        AlarmLevelList = null;
        AlarmLevelList = tempList;

DoPropertyChanged(SelectedAlarmLevelPropertyName);
}

Original Comments

Feedback Bot on 5/21/2024, 10:54 AM:

(private comment, text removed)


Original Solutions

(no solutions)

miloush commented 4 months ago

Can you please explain what are you trying to achieve?

Dragan-Radovac commented 4 months ago

Hey brother, I've made some subtle changes that have the converter executing when expected

using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Windows;
using System.Windows.Data;

namespace WpfApp1
{
    public enum AlarmLevel
    {
        [Description("正常")]
        Normal = 0,

        [Description("警报")]
        Warring = 1,

        [Description("故障")]
        Error = 2,
    }

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public static EnumDescriptionConverter EnumDescriptionConverter { get; set; } = new();

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            InitList();
        }

        public List<AlarmLevel> AlarmLevelList
        {
            get
            {
                return _alarmLevelList;
            }
            set
            {
                _alarmLevelList = value;
                DoPropertyChanged(AlarmLevelListPropertyName);
            }
        }

        private List<AlarmLevel> _alarmLevelList = [];
        public const string AlarmLevelListPropertyName = "AlarmLevelList";

        public AlarmLevel SelectedAlarmLevel
        {
            get { return _selectedAlarmLevel; }
            set
            {
                if (_selectedAlarmLevel != value)
                {
                    _selectedAlarmLevel = value;
                    DoPropertyChanged(SelectedAlarmLevelPropertyName);
                }
            }
        }
        private AlarmLevel _selectedAlarmLevel;
        public const string SelectedAlarmLevelPropertyName = "SelectedAlarmLevel";

        public void InitList()
        {
        AlarmLevelList = new List<AlarmLevel>();
            AlarmLevelList.Add(AlarmLevel.Normal);
            AlarmLevelList.Add(AlarmLevel.Warring);
            AlarmLevelList.Add(AlarmLevel.Error);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            DoPropertyChanged(AlarmLevelListPropertyName);
            DoPropertyChanged(SelectedAlarmLevelPropertyName);
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        protected void DoPropertyChanged(string propertyName = "")
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class EnumDescriptionConverter : IValueConverter
    {
        object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value != null && value as string != "")
            {
                Enum myEnum = (Enum)value;
                string description = GetEnumDescription(myEnum);
                return description;
            }

            return "";
        }

        object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return string.Empty;
        }

        private string GetEnumDescription(Enum enumObj)
        {
            FieldInfo? fieldInfo = enumObj.GetType().GetField(enumObj.ToString());

            object[] attribArray = fieldInfo!.GetCustomAttributes(false);

            if (attribArray.Length == 0)
            {
                return enumObj.ToString();
            }
            else
            {
            DescriptionAttribute? attrib = attribArray[0] as DescriptionAttribute;
                return attrib!.Description;
            }
        }
    }
}

So above, I changed EnumDescriptionConverter to a static property on the Window.

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <ComboBox Width="200" Height="50" Margin="100"
          HorizontalAlignment="Center"
          VerticalAlignment="Center"
          ItemsSource="{Binding AlarmLevelList}"
          SelectedItem="{Binding SelectedAlarmLevel}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Converter={x:Static local:MainWindow.EnumDescriptionConverter}}" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>

        <Button Content="OK" Width="100" Height="30" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click"/>
    </Grid>
</Window>

In the xaml you will see the Converter set in the Binding using x:Static markup extension.

Best wishes