0

I'm trying to make a user control in WPF (using xceed). I have a combobox and an integerUpDown.

A label should change its content when one of those changes its value. First label has just a simple binding to the integerUpDown, that's working. Visual Studio created OnPropertyChanged() and getters/setters automatically and I try to use it to bind my result to the second label but it's not working. Nothing happens.

This is my XAML:

<UserControl x:Class="WpfApp2.CardSelectionControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApp2" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">    

        <StackPanel Margin="5" Width="300" Height="400" Background="LightGray">
            
        <TextBox Margin="5" AcceptsReturn="True" FontSize="20" Padding="5" Height="70">placeholding</TextBox>

        <ComboBox SelectedIndex="{Binding SelectedIndex}" Background="Red" Margin="5">
                <ComboBoxItem Content="card1"></ComboBoxItem>
                <ComboBoxItem Content="card2"></ComboBoxItem>
        </ComboBox>

        <xctk:UIntegerUpDown Margin="5" x:Name="upDown" Value="{Binding Level}" ></xctk:UIntegerUpDown>

        <Label Height="50" Content="{Binding Level}"></Label>
        <Label Height="50" Content="{Binding Result}" ></Label>
            
        </StackPanel>
</UserControl>

Code behind:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;

namespace WpfApp2
{
    public partial class CardSelectionControl : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public int Level
        {
            get { OnPropertyChanged(); return (int)GetValue(LevelProperty); }
            set
            {
                OnPropertyChanged();
                SetValue(LevelProperty, value);
            }
        }

        public int SelectedIndex
        {
            get { OnPropertyChanged();  return (int)GetValue(SelectedIndexProperty); }
            set
            {
                OnPropertyChanged();
                SetValue(SelectedIndexProperty, value);
            }
        }

        public int Result
        {
            get { return (int)GetValue(ResultProperty); }
            set { SetValue(ResultProperty, value); }
        }

        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (SelectedIndex == 0) 
            { Result = Level * 2; }
            else if (SelectedIndex == 1) 
            { Result = Level * 3; }
        }

        public static readonly DependencyProperty ResultProperty =
            DependencyProperty.Register("Result", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(20));

        public static readonly DependencyProperty LevelProperty =
            DependencyProperty.Register("Level", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(10));

        public static readonly DependencyProperty SelectedIndexProperty =
            DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(0));

        public CardSelectionControl()
        {
            DataContext = this;
            InitializeComponent();
        }
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Kappe619
  • 15
  • 4
  • A UserControl should never explicitly set its own DataContext, because that would break any standard DataContext-based Bindings of its properties, like e.g. ``. It should instead use RelativeSource Bindings in its XAML, as shown in the answer to duplicate question. – Clemens May 29 '22 at 08:37

1 Answers1

1

I assume you expect your setters to get called, hence why you are calling OnPropertyChanged everywhere. They don't get called so your code won't be executed.

Instead, you can add a callback to your dependency properties so you know when the values get changed:

public static readonly DependencyProperty LevelProperty =
    DependencyProperty.Register("Level", 
                                typeof(int), 
                                typeof(CardSelectionControl), 
                                new PropertyMetadata(10, new PropertyChangedCallback(OnChanged)));
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var cardSelectionControl = (CardSelectionControl)d;
    cardSelectionControl.Recalculate();
}

I took the liberty of changing OnPropertyChanged to Recalculate, added a PropertyChangedCallback for the combobox as well and the entire code becomes:

public partial class CardSelectionControl : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public int Level
    {
        get { return (int)GetValue(LevelProperty); }
        set { SetValue(LevelProperty, value); }
    }

    public int SelectedIndex
    {
        get { return (int)GetValue(SelectedIndexProperty); }
        set { SetValue(SelectedIndexProperty, value); }
    }

    public int Result
    {
        get { return (int)GetValue(ResultProperty); }
        set { SetValue(ResultProperty, value); }
    }

    public void Recalculate()
    {
        if (SelectedIndex == 0)
            Result = Level * 2;
        else if (SelectedIndex == 1)
            Result = Level * 3;
    }

    public static readonly DependencyProperty ResultProperty =
        DependencyProperty.Register("Result", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(20));

    public static readonly DependencyProperty LevelProperty =
        DependencyProperty.Register("Level", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(10, new PropertyChangedCallback(OnChanged)));

    public static readonly DependencyProperty SelectedIndexProperty =
        DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(0, new PropertyChangedCallback(OnChanged)));

    private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var cardSelectionControl = (CardSelectionControl)d;
        cardSelectionControl.Recalculate();
    }

    public CardSelectionControl()
    {
        DataContext = this;
        InitializeComponent();
    }
}
user1969903
  • 810
  • 13
  • 26