Thursday, March 3, 2011

How do I get rid of the red rectangle when my wpf binding validation has failed and the containing panel is no longer visible?

I have a situation where I am using wpf data binding and validation using the ExceptionValidationRule.

Another part of the solution invovles collapsing some panels and showing others.

If a validation exception is set - i.e. the UI is showing a red border around the UI element with the validation problem, and the containing panel is collapsed, the red border is still displayed. This is clearly not meant to be? Is there a workaround for this? Anyone know if this is by design?

Minimal code example provided (not my actual code, but replicates the problem). Create a new WpfApplication (I called mine WpfDataBindingProblem).

The xaml for window1 is as follows:

<Window x:Class="WpfDataBindingProblem.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel Margin="5">

        <StackPanel Name="panel1" Visibility="Visible" Margin="5">
            <TextBox Name="DataBoundTextBox">
                <Binding Path="TextValue">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule/>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox>
        </StackPanel>

        <StackPanel Name="panel2" Visibility="Collapsed" Margin="5">
            <TextBlock>
                The quick brown fox jumps over the lazy dog.
            </TextBlock>
        </StackPanel>

        <Button Click="Button_Click" Margin="5">
            Toggle panels
        </Button>

    </StackPanel>
</Window>

The code for window1 is as follows:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfDataBindingProblem {

    public partial class Window1 : Window {
        public Window1() {
            InitializeComponent();

            this.DataContext = new MyClass("default");
        }

        private void Button_Click(object sender, RoutedEventArgs e) {
            panel1.Visibility = panel1.Visibility == Visibility.Collapsed ?
                Visibility.Visible : Visibility.Collapsed;
            panel2.Visibility = panel2.Visibility == Visibility.Collapsed ?
                Visibility.Visible : Visibility.Collapsed;
        }
    }

    public class MyClass : INotifyPropertyChanged {

        private string mTextValue;

        public MyClass(string defaultText) {
            TextValue = defaultText;
        }

        public string TextValue {
            get {
                return mTextValue;
            }
            set {
                mTextValue = value;
                if (string.IsNullOrEmpty(mTextValue)) {
                    throw new ApplicationException("Text value cannot be empty");
                }
                OnPropertyChanged(new PropertyChangedEventArgs("TextValue"));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
            if (this.PropertyChanged != null) {
                this.PropertyChanged(this, e);
            }
        }
    }

}

To reproduce the problem, run the application. Delete the default text from the textbox and tab off - red rectangle is shown indicating a validation problem. Click the button. Panel containing control with red rectangle is hidden and another panel is shown, but the red rectangle remains. Aargh!

All help much appreciated.

PS apologies for long question title!

From stackoverflow
  • I have an answer to the problem myself which is to change my button click event which changes the visibility of the panels. This would change to something like this:

    private void Button_Click(object sender, RoutedEventArgs e) {
        if (panel1.Visibility == Visibility.Collapsed) {
            panel1.Visibility = Visibility.Visible;
            DataBoundTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
            panel2.Visibility = Visibility.Collapsed;
        }
        else {
            panel1.Visibility = Visibility.Collapsed;
            DataBoundTextBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
            panel2.Visibility = Visibility.Visible;
        }
    }
    

    The UpdateSource() and UpdateTarget() have the effect of reapplying and removing the red rectangle, but this seems like an ugly hack. Surely the wpf framework should be hiding the red rectangle for me when the containing panel is collapsed. Any cleaner fix that doesn't require me to fiddle with the binding expression gets my vote.

    Thanks,

    Sam

  • If I remember correctly, this is a known issue. We re-templated textbox to include the following:

    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <ControlTemplate.Resources>
                    <BooleanToVisibilityConverter x:Key="converter" />
            </ControlTemplate.Resources>
                <DockPanel LastChildFill="True">
                    <Border 
                        BorderThickness="1"
                        BorderBrush="Red"
                        Visibility="{Binding ElementName=placeholder, Mode=OneWay, Path=AdornedElement.IsVisible, Converter={StaticResource converter}}"
                        >
                        <AdornedElementPlaceholder x:Name="placeholder" />
                    </Border>
                 </DockPanel>
             </ControlTemplate>
        </Setter.Value>
    </Setter>
    

    Sam Meldrum : Donnelle, This is fantastic! Exactly what I need. Many thanks, Sam
    Donnelle : You're welcome. Glad to help!
    Pwninstein : That's amazingly simple. Thanks!!!

0 comments:

Post a Comment