savi
savi

Reputation: 537

Implementing INotifyPropertyChanged in C# WPF

I am pretty new to C# and WPF framework,my question might sound silly. I am using the two-way binding feature. So I implement INotifyPropertyChanged interface. I have a property named DisplayFormat, so whenever the format changes from Binary to Hex, my text should convert accordingly. My question is where should I include the code/logic for the conversion between Binary and decimal?

[DefaultValue(displayFormat.Binary)]
public displayFormat DisplayFormat
{
    get { return this.format; }
    set { this.format = value; OnPropertyChanged("DisplayFormat"); }
}

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string name)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(name));
    }
}

Upvotes: 3

Views: 893

Answers (3)

BradleyDotNET
BradleyDotNET

Reputation: 61369

Another converter approach would be to use a MultiValueConverter. This does not require the dependency property or additional NotifyPropertyChanged. The important thing to know about MultiValueConverter is that you get the "values" in the order that you put them in XAML.

The converter becomes:

public class NumberToSpecialStringConverter : IMultiValueConverter
{
    public Convert(...)
    {
       //This is going to the UI, from the Model
       return (value[1] as DisplayFormat).Convert(value[0]); //Or whatever you have
    }

    public ConvertBack(...)
    {
       //This is going to the Model, from the UI
       return (value[1] as DisplayFormat).ConvertBack(value[0]); //Or whatever you have
    }
}

The XAML:

<Window.Resources>
   <local:NumberToSpecialStringConverter x:Key="FormatConverter"/>
</Window.Resources>
...
<TextBlock>
   <MultiBinding Converter="{StaticResource FormatConverter}">
      <Binding Path="MyValue"/>
      <Binding Path="DisplayFormat"/>
   </MultiBinding>
</TextBlock>

No other changes are required.

Upvotes: 2

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149608

What can be done is have two properties, both binded to your DisplayFormat property. When a Hex value should be presented, you may set the visibility of the Binary content control (maybe a TextBlock) to false and only present your Hex values TextBlock, and the other way around for Binary values. This can be achieved using an EventTrigger on the NotifyPropertyChanged of DisplayFormat.

Another approach would be to use a IValueConverter on your DisplayFormat property and have the logic for checking Hex or Binary and returning the proper format. That way you may present tge appropriate format for each value.

A possible IValueConverter implementation:

public class BinaryOrHexConverter : IValueConverter    
{        
    public object Convert(object value, Type targetType, object parameter,
                  System.Globalization.CultureInfo culture)        
    {
        int result;
        return int.TryParse(inputString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result) ? result.ToString() : value.ToString() // Assuming default is binary
    }

public object ConvertBack(object value, Type targetType, object parameter,
                System.Globalization.CultureInfo culture)
{
    return DependencyProperty.UnsetValue;
}            

}

Upvotes: 1

BradleyDotNET
BradleyDotNET

Reputation: 61369

Whenever you need to convert something to something else, you use a converter. A converter is just a class that implements IValueConverter.

public class NumberToSpecialStringConverter : IValueConverter
{
   ...
}

Converters take two inputs, the value (whatever is actually displayed or bound to) and a parameter. The parameter can be whatever you want, but cannot be bound. Since you want a bound parameter, we need to inherit from DependencyObject as well and declare a DependencyProperty to bind to:

public class NumberToSpecialStringConverter : DependencyObject, IValueConverter
{
    public displayFormat CurrentFormat
    { //Dependency property stuff }

    //Other DP stuff, use the 'propdp' snippet to get it all, or look on MSDN

    public Convert(...)
    {
       //This is going to the UI, from the Model
       return displayFormat.Convert(value); //Or whatever you have
    }

    public ConvertBack(...)
    {
       //This is going to the Model, from the UI
       return displayFormat.ConvertBack(value); //Or whatever you have
    }
}

Now that you have that, you just need to declare and use it:

<Window.Resources>
   <local:NumberToSpecialStringConverter x:Key="FormatConverter" CurrentFormat="{Binding DisplayFormat}"/>
</Window.Resources>
...
<TextBlock Text="{Binding Path=MyValue, Converter={StaticResource FormatConverter}"/>

There is one more catch. Changing "DisplayFormat" will cause NotifyPropertyChanged to fire, which will update the value inside the converter. However, the UI isn't running the convert function because it doesn't think anything has changed. Thus, you need to "pretend" that the value changed by invoking its NotifyPropertyChanged as well:

[DefaultValue(displayFormat.Binary)]
public displayFormat DisplayFormat
{
    get { return this.format; }
    set
    { 
        this.format = value; 
        OnPropertyChanged("DisplayFormat"); 
        OnPropertyChanged("MyValue");
    }
}

Now the UI will perform a fresh "get" and conversion and display what you wanted!

Upvotes: 1

Related Questions