Home > OS >  WinUI-3 and C# <> Clarification with regards to: How to work around UpdateSourceTrigger ignore
WinUI-3 and C# <> Clarification with regards to: How to work around UpdateSourceTrigger ignore

Time:10-11

UPDATE: Thank You Andrew KeepCoding for the CustomNumberBox Class. This was really helpful and much easier to use then the other solution. It works without data bindings.

I have a clarification/user error with regards to the question asked by Chris Schaller and answered by Richard Zhang: How to work around UpdateSourceTrigger ignored in NumberBox

I am receiving the following two errors:

Error CS1061 'NumberBox' does not contain a definition for 'VisualTreeFindName' and no accessible extension method 'VisualTreeFindName' accepting a first argument of type 'NumberBox' could be found (are you missing a using directive or an assembly reference?)

Error CS0103 The name 'Model' does not exist in the current context here

Here is the extension from the previous answer (though mine is namespace accounting..)

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using System;

namespace Accounting
{
    public static class StaticExtension
    {
        public static FrameworkElement VisualTreeFindName(this DependencyObject element, string name)
        {
            if (element == null || string.IsNullOrWhiteSpace(name))
            {
                return null;
            }
            if (name.Equals((element as FrameworkElement)?.Name, StringComparison.OrdinalIgnoreCase))
            {
                return element as FrameworkElement;
            }
            var childCount = VisualTreeHelper.GetChildrenCount(element);
            for (int i = 0; i < childCount; i  )
            {
                var result = VisualTreeHelper.GetChild(element, i).VisualTreeFindName(name);
                if (result != null)
                {
                    return result;
                }
            }
            return null;
        }
    }
}

Here is MainWindow.xaml

<Window
    x:Class="Accounting.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Accounting"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <NumberBox
            x:Name="myNumberBox"
            SpinButtonPlacementMode="Compact"
            Loaded="NumberBox_Loaded"
            Value="0" />
    </StackPanel>
</Window>

Here is MainWindow.xaml.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System;
using System.Linq;
using System.Reflection; 

namespace Accounting
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }

        private void NumberBox_Loaded(object sender, RoutedEventArgs e)
        {
            var box = sender as NumberBox;
            var textBox = box.VisualTreeFindName<TextBox>("InputBox");
            textBox.TextChanged  = TextBox_TextChanged;
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            string text = (sender as TextBox).Text;
            bool isNumber = !text.Any(t => !char.IsDigit(t));
            if (isNumber)
            {
                double.TryParse(text, out double value);
                if (value != Model.Value)
                    Model.Value = value;
            }
        }
    }
}

CodePudding user response:

You can also create a custom control like this. One drawback here is that it will always work as UpdateSourceTrigger=PropertyChanged.

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System.Collections.Generic;
using System.Linq;

namespace NumberBoxes;

public class CustomNumberBox : NumberBox
{
    public CustomNumberBox()
    {
        Loaded  = CustomNumberBox_Loaded;
    }

    private static IEnumerable<T> FindChildrenOfType<T>(DependencyObject parent) where T : DependencyObject
    {
        if (parent is ContentControl contentControl)
        {
            if (contentControl.Content is T tContent)
            {
                yield return tContent;
            }

            if (contentControl.Content is DependencyObject dependencyObjectContent)
            {
                foreach (T grandChild in FindChildrenOfType<T>(dependencyObjectContent))
                {
                    yield return grandChild;
                }
            }
        }
        else
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i  )
            {
                DependencyObject child = VisualTreeHelper.GetChild(parent, i);

                if (child is T tChild)
                {
                    yield return tChild;
                }

                foreach (T grandChild in FindChildrenOfType<T>(child))
                {
                    yield return grandChild;
                }
            }
        }
    }

    private void CustomNumberBox_Loaded(object sender, RoutedEventArgs e)
    {
        if (FindChildrenOfType<TextBox>(this)
            .Where(x => x.Name is "InputBox")
            .FirstOrDefault() is TextBox inputBox)
        {
            inputBox.TextChanged  = InputBox_TextChanged;
        }
    }

    private void InputBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (sender is TextBox inputBox)
        {
            Text = inputBox.Text;
        }
    }
}

CodePudding user response:

Error CS1061 'NumberBox' does not contain a definition for 'VisualTreeFindName' and no accessible extension method 'VisualTreeFindName' accepting a first argument of type 'NumberBox' could be found (are you missing a using directive or an assembly reference?)

You get this error because the method VisualTreeFindName is not a generic one and doesn't want <TextBox> when you call it. Removing <TextBox> and casting the return to a TextBox should work and give you the TextBox named InputBox inside the NumberBox.

if (this.ThisNumberBox.VisualTreeFindName("InputBox") is TextBox inputBox)
{
    inputBox.TextChanged  = InputBox_TextChanged;
}

Error CS0103 The name 'Model' does not exist in the current context here

You get this error because Model does not exist in your code. This Model is just a property used in the question you are using as a reference. Replacing Model with your target property should do the trick.

  • Related