0

I have a StackPanel of basic input controls. Some combinations selections are invalid. I would like to recompute the "form"s validation every time one of the inputs change. Is there some kind of event you can listen to on the parent control that will trigger when ever an observable property changes on a child?

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <RadioButtons Header="Options:">
        <RadioButton Content="Option 1"/>
        <RadioButton Content="Option 2"/>
    </RadioButtons>
    <ToggleSwitch OnContent="On" OffContent="Off"/>
    <ComboBox>
        <ComboBoxItem Content="Item 1"/>
        <ComboBoxItem Content="Item 2"/>
    </ComboBox>
</StackPanel>
Tom Huntington
  • 2,260
  • 10
  • 20

2 Answers2

1

It is recommended to use Data Binding to listen the selection change of the controls. In Data Binding, use Binding.Mode TwoWay bindings, changes to the target will automatically propagate to the source.

Define a class which inherits INotifyPropertyChanged, and recompute the "form"s validation in the PropertyChanged event.

Page.xaml

<Grid>
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
        <controls:RadioButtons Header="Options:" SelectedIndex="{x:Bind selectItems.RadioButtonsIndex, Mode=TwoWay}">
            <RadioButton Content="Option 1"/>
            <RadioButton Content="Option 2"/>
        </controls:RadioButtons>
        <ToggleSwitch OnContent="On" OffContent="Off" IsOn="{x:Bind selectItems.ToggleSwitchState, Mode=TwoWay}"/>
        <ComboBox SelectedIndex="{x:Bind selectItems.ComboBoxSelectIndex, Mode=TwoWay}">
            <ComboBoxItem Content="Item 1"/>
            <ComboBoxItem Content="Item 2"/>
        </ComboBox>
    </StackPanel>
</Grid>

Page.xaml.cs

public sealed partial class MainPage : Page
{
    public SelectItems selectItems = new SelectItems();
    public MainPage()
    {
        this.InitializeComponent();
        selectItems.PropertyChanged += SelectItems_PropertyChanged;
    }

    private void SelectItems_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (selectItems.RadioButtonsIndex == 1 
            && selectItems.ToggleSwitchState == true 
            && selectItems.ComboBoxSelectIndex == 1) 
        {
            Debug.WriteLine("Invalid combinations selections");
        }
    }
}

public class SelectItems: INotifyPropertyChanged
{
    private int _radioButtonsIndex;
    private bool _toggleSwitchState;
    private int _comboBoxSelectIndex;

    public SelectItems()
    {
        RadioButtonsIndex = 0;
        ToggleSwitchState= false; 
        ComboBoxSelectIndex= 0;
    }
    public int RadioButtonsIndex
    {
        get => _radioButtonsIndex;
        set
        {
            _radioButtonsIndex = value;
            NotifyPropertyChanged();
        }
    }

    public bool ToggleSwitchState
    {
        get => _toggleSwitchState;
        set
        {
            _toggleSwitchState = value;
            NotifyPropertyChanged();
        }
    }

    public int ComboBoxSelectIndex
    {
        get => _comboBoxSelectIndex;
        set
        {
            _comboBoxSelectIndex = value;
            NotifyPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
Junjie Zhu - MSFT
  • 2,086
  • 1
  • 2
  • 6
  • 1
    Thanks, I don't really like duplicating of state contained in the view. But I suppose UWP is a databinding framework so this I how you are meant to program. – Tom Huntington Jun 09 '23 at 08:35
0

I guess the best thing to do is to register handlers on each child control which all raise the same event.

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
    <RadioButtons Header="Options:" SelectionChanged="SelectionChanged">
        <RadioButton Content="Option 1"/>
        <RadioButton Content="Option 2"/>
    </RadioButtons>
    <ToggleSwitch x:Name="MyToggle" Toggled="MyToggle_Toggled" OnContent="On" OffContent="Off"/>
    <ComboBox SelectionChanged="SelectionChanged">
        <ComboBoxItem Content="Item 1"/>
        <ComboBoxItem Content="Item 2"/>
    </ComboBox>
</StackPanel>
winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;

void winrt::App1::implementation::MainWindow::MyToggle_Toggled(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
    m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"[PropertyName]" });
}

void winrt::App1::implementation::MainWindow::SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e)
{
    m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"[PropertyName]" });
}

Then you can listen to this event.

Tom Huntington
  • 2,260
  • 10
  • 20
  • Inside a `UserControl`, you probably would rather author an `Windows.Foundation.EventHandler` event rather than `PropertyChangedEventHandler` https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/author-events and register a click handler on the xaml – Tom Huntington Jun 10 '23 at 01:44