如何强制验证在 UserControl 上显示

发布于 2024-12-12 10:40:32 字数 7367 浏览 0 评论 0原文

我想要带有工具提示的库存/标准红色边框验证在我的用户控件周围显示。请参阅下面的代码。我有主页和用户控件。

UserControl 有文本框和按钮。 UserControls 绑定到 Id 属性并在 TextBox 中显示此 Id。

主页有 UserControl 和 TextBox。它们绑定到 FirstValue 和 SecondValue。这两个属性都会引发错误。当我在文本框中输入/更改某些内容时 - 我看到边框和摘要。当我更改用户控件中的文本时 - 我在摘要中看到错误,但没有边框,当我单击错误时 - 它聚焦按钮,不会转到文本框。我该如何解决这个问题?我希望整个 UserControl 位于红色边框内。

MainPage XAML:

<UserControl x:Class="SilverlightApplication1.MainPage"
    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"
    mc:Ignorable="d"
    d:DesignHeight="275" d:DesignWidth="402" xmlns:my="clr-namespace:SilverlightApplication1" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White" Width="300" HorizontalAlignment="Left">
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="40" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBox Grid.Row="2" Margin="3" Text="{Binding SecondValue, Mode=TwoWay, NotifyOnValidationError=True}"/>
        <my:TestUserControl Margin="3" Id="{Binding FirstValue, Mode=TwoWay, NotifyOnValidationError=True}"/>
        <sdk:ValidationSummary Grid.Row="4" Name="validationSummary1" />
    </Grid>
</UserControl>

MainPage CS

using System.Windows.Controls;


namespace SilverlightApplication1
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;

    public partial class MainPage : UserControl, INotifyDataErrorInfo, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
        readonly Dictionary<string, List<string>> _currentErrors;

        private string _firstValue;
        private string _secondValue;

        public MainPage()
        {
            InitializeComponent();
            _currentErrors = new Dictionary<string, List<string>>();
            _firstValue = "test1";
            _secondValue = "test2";
            LayoutRoot.DataContext = this;
        }


        public string FirstValue
        {
            get { return _firstValue; }

            set
            {
                _firstValue = value;
                CheckIfValueIsValid("FirstValue", value);
                this.OnPropertyChanged("FirstValue");
            }
        }

        public string SecondValue
        {
            get { return _secondValue; }
            set
            {
                _secondValue = value;
                CheckIfValueIsValid("SecondValue", value);
                this.OnPropertyChanged("SecondValue");
            }
        }

        public void CheckIfValueIsValid(string propertyName, string value)
        {
            ClearErrorFromProperty(propertyName);

            AddErrorForProperty(propertyName, "Bad value");
        }

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

        public IEnumerable GetErrors(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                return (_currentErrors.Values);
            }
            MakeOrCreatePropertyErrorList(propertyName);

            return _currentErrors[propertyName];
        }

        public bool HasErrors
        {
            get
            {
                return (_currentErrors.Where(c => c.Value.Count > 0).Count() > 0);
            }
        }

        void FireErrorsChanged(string property)
        {
            if (ErrorsChanged != null)
            {
                ErrorsChanged(this, new DataErrorsChangedEventArgs(property));
            }
        }

        public void ClearErrorFromProperty(string property)
        {
            MakeOrCreatePropertyErrorList(property);

            _currentErrors[property].Clear();
            FireErrorsChanged(property);
        }

        public void AddErrorForProperty(string property, string error)
        {
            MakeOrCreatePropertyErrorList(property);
            _currentErrors[property].Add(error);
            FireErrorsChanged(property);
        }

        void MakeOrCreatePropertyErrorList(string propertyName)
        {
            if (!_currentErrors.ContainsKey(propertyName))
            {
                _currentErrors[propertyName] = new List<string>();
            }
        }


    }
}

UserControl XAML:

<UserControl x:Class="SilverlightApplication1.TestUserControl"
    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"
    mc:Ignorable="d"
    d:DesignHeight="30" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Button Content="..." Grid.Column="1" Padding="10,0" />
        <TextBox Text="{Binding Id, Mode=TwoWay}" />
    </Grid>
</UserControl>

UserControl CS:

using System.Windows.Controls;

namespace SilverlightApplication1
{
    using System.ComponentModel;
    using System.Windows;

    public partial class TestUserControl : UserControl, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public TestUserControl()
        {
            InitializeComponent();
            LayoutRoot.DataContext = this;
        }


        public string Id
        {
            get { return (string)base.GetValue(IdProperty); }
            set
            {
                base.SetValue(IdProperty, value);
                this.OnPropertyChanged("Id");
            }
        }

        public static DependencyProperty IdProperty =
            DependencyProperty.Register(
            "Id",
            typeof(string),
            typeof(TestUserControl),
            new PropertyMetadata(OnIdChanged));

        private static void OnIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (DesignerProperties.IsInDesignTool) return;

            var lookup = d as TestUserControl;
            lookup.OnPropertyChanged("Id");
        }

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

    }
}

I want stock/standard red border validation with tooltip to show around my UserControl. See code below. I have main page and UserControl.

UserControl has textbox and button. UserControls binds to Id property and displays this Id inside TextBox.

Main page has UserControl and TextBox. They bound to FirstValue and SecondValue. Both properties raise error. When I type/change something in textbox - I see border and summary. When I change text in my UserControl - I see error in summary but no border and when I click on error - it focuses button, doesn't go to TextBox. How do I fix that? I want whole UserControl to be inside red border.

MainPage XAML:

<UserControl x:Class="SilverlightApplication1.MainPage"
    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"
    mc:Ignorable="d"
    d:DesignHeight="275" d:DesignWidth="402" xmlns:my="clr-namespace:SilverlightApplication1" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White" Width="300" HorizontalAlignment="Left">
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="40" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBox Grid.Row="2" Margin="3" Text="{Binding SecondValue, Mode=TwoWay, NotifyOnValidationError=True}"/>
        <my:TestUserControl Margin="3" Id="{Binding FirstValue, Mode=TwoWay, NotifyOnValidationError=True}"/>
        <sdk:ValidationSummary Grid.Row="4" Name="validationSummary1" />
    </Grid>
</UserControl>

MainPage CS

using System.Windows.Controls;


namespace SilverlightApplication1
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;

    public partial class MainPage : UserControl, INotifyDataErrorInfo, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
        readonly Dictionary<string, List<string>> _currentErrors;

        private string _firstValue;
        private string _secondValue;

        public MainPage()
        {
            InitializeComponent();
            _currentErrors = new Dictionary<string, List<string>>();
            _firstValue = "test1";
            _secondValue = "test2";
            LayoutRoot.DataContext = this;
        }


        public string FirstValue
        {
            get { return _firstValue; }

            set
            {
                _firstValue = value;
                CheckIfValueIsValid("FirstValue", value);
                this.OnPropertyChanged("FirstValue");
            }
        }

        public string SecondValue
        {
            get { return _secondValue; }
            set
            {
                _secondValue = value;
                CheckIfValueIsValid("SecondValue", value);
                this.OnPropertyChanged("SecondValue");
            }
        }

        public void CheckIfValueIsValid(string propertyName, string value)
        {
            ClearErrorFromProperty(propertyName);

            AddErrorForProperty(propertyName, "Bad value");
        }

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

        public IEnumerable GetErrors(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                return (_currentErrors.Values);
            }
            MakeOrCreatePropertyErrorList(propertyName);

            return _currentErrors[propertyName];
        }

        public bool HasErrors
        {
            get
            {
                return (_currentErrors.Where(c => c.Value.Count > 0).Count() > 0);
            }
        }

        void FireErrorsChanged(string property)
        {
            if (ErrorsChanged != null)
            {
                ErrorsChanged(this, new DataErrorsChangedEventArgs(property));
            }
        }

        public void ClearErrorFromProperty(string property)
        {
            MakeOrCreatePropertyErrorList(property);

            _currentErrors[property].Clear();
            FireErrorsChanged(property);
        }

        public void AddErrorForProperty(string property, string error)
        {
            MakeOrCreatePropertyErrorList(property);
            _currentErrors[property].Add(error);
            FireErrorsChanged(property);
        }

        void MakeOrCreatePropertyErrorList(string propertyName)
        {
            if (!_currentErrors.ContainsKey(propertyName))
            {
                _currentErrors[propertyName] = new List<string>();
            }
        }


    }
}

UserControl XAML:

<UserControl x:Class="SilverlightApplication1.TestUserControl"
    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"
    mc:Ignorable="d"
    d:DesignHeight="30" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Button Content="..." Grid.Column="1" Padding="10,0" />
        <TextBox Text="{Binding Id, Mode=TwoWay}" />
    </Grid>
</UserControl>

UserControl CS:

using System.Windows.Controls;

namespace SilverlightApplication1
{
    using System.ComponentModel;
    using System.Windows;

    public partial class TestUserControl : UserControl, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public TestUserControl()
        {
            InitializeComponent();
            LayoutRoot.DataContext = this;
        }


        public string Id
        {
            get { return (string)base.GetValue(IdProperty); }
            set
            {
                base.SetValue(IdProperty, value);
                this.OnPropertyChanged("Id");
            }
        }

        public static DependencyProperty IdProperty =
            DependencyProperty.Register(
            "Id",
            typeof(string),
            typeof(TestUserControl),
            new PropertyMetadata(OnIdChanged));

        private static void OnIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (DesignerProperties.IsInDesignTool) return;

            var lookup = d as TestUserControl;
            lookup.OnPropertyChanged("Id");
        }

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

    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

回忆凄美了谁 2024-12-19 10:40:32

为了使您的示例正常工作,您应该从 TestUserControl.xaml.cs 文件中删除所有代码并修复绑定。像这样:

<Button Content="..." Grid.Column="1" Padding="10,0" />
<TextBox Text="{Binding FirstValue, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True}" />

后面是空代码:

public partial class TestUserControl : UserControl
{
    public TestUserControl()
    {
        InitializeComponent();
    }
}

接下来,你的另一个问题

在所有 3 个框(一个矩形)周围显示红色边框

可以通过绑定到 HasError 属性来实现。根据您的代码,它看起来是这样的:

<Grid x:Name="LayoutRoot" Background="White">
   <!-- ... -->
   <Border BorderBrush="Red" BorderThickness="1" 
            Grid.ColumnSpan="2" IsHitTestVisible="False"
            Visibility="{Binding HasErrors, Converter={StaticResource BooleanToVisibilityConverter}}" />

我添加了红色的 Border 元素,当 HasError 属性设置为 true 时,该元素变得可见。但是您应该在代码中的某个位置调用 OnPropertyChanged("HasError")

另一种方法:创建自定义控件而不是 UserControl。
我已经发布了过多的描述如何在自定义控件中实现验证作为 这个关于验证的问题
 

我可以做出更具体的答案,但是您应该通过将视图模型与视图分离来修复帖子中的代码,因为现在很难根据当前代码实现某些内容。
检查我关于使用 INotifyDataErrorInfo 实现验证的帖子: WPF 和 Silverlight验证

或者您可以直接下载我的验证类

之后代码会简单得多,我将能够帮助您解决更复杂的问题。

To make your example work, you should remove all the code from the TestUserControl.xaml.cs file and fix bindings. Like this:

<Button Content="..." Grid.Column="1" Padding="10,0" />
<TextBox Text="{Binding FirstValue, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True}" />

And empty code behind:

public partial class TestUserControl : UserControl
{
    public TestUserControl()
    {
        InitializeComponent();
    }
}

Next, your another question

display red border around all 3 boxes (one rectangle)

It can be achieved by binding to the HasError property. According to your code it will look so:

<Grid x:Name="LayoutRoot" Background="White">
   <!-- ... -->
   <Border BorderBrush="Red" BorderThickness="1" 
            Grid.ColumnSpan="2" IsHitTestVisible="False"
            Visibility="{Binding HasErrors, Converter={StaticResource BooleanToVisibilityConverter}}" />

I've added the red Border element which becomes visible when the HasError property is set to true. But you should call the OnPropertyChanged("HasError") somewhere in your code.

Another way: to create a custom control instead of UserControl.
I've posted excessive description how to implement validation in custom controls as the answer to this question about validation.  

I can make a more concrete answer, but you should fix the code in your post by separating view model from view, because now it is difficult to implement something based on current code.
Check my post about implementation of validation using INotifyDataErrorInfo: WPF and Silverlight Validation.

Or you can download my validation classes directly.

After that the code will be much simplier, and I will be able to help you with more complex questions.

苦妄 2024-12-19 10:40:32

更改

<TextBox Text="{Binding Id, Mode=TwoWay}" />

更改为

<TextBox Text="{Binding Id, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />

Change

<TextBox Text="{Binding Id, Mode=TwoWay}" />

to

<TextBox Text="{Binding Id, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文