tags:

views:

742

answers:

2

I have a Textbox which is bound to my data object. If the validation fails I would like to show a popup which contains the error message. In XAML this works fine. I'm using the following XAML:

<TextBox Height="23" Margin="54,12,104,0" Name="textBox1" 
VerticalAlignment="Top" Text="{Binding Value, ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged}"></TextBox>

        <Popup Name="myPopup" PlacementTarget="{Binding ElementName=textBox1}"
                       IsOpen="{Binding ElementName=textBox1, Path=(Validation.HasError), Mode=OneWay}"
                       >
            <TextBlock Name="myPopupText" Background="LightBlue" Foreground="Blue">
                        The value is invalid
            </TextBlock>
        </Popup>

My problem is that I have to create the popup and binding in code and I cannot get it to work. I have tried several different options. I also used dummy converter just to see whether the binding works at all. It seems that the binding works when I create it (it gets the initial value) but after that nothing happens. I can see that the Validation.HasError updates correctly (TextBox's border turns red), but that's it. My dummy converter is not called. Here is the code I'm using:

    Popup popup = new Popup();
    popup.Name = "somepopup";
    // Source is the textbox which is bound to the data object
    popup.PlacementTarget = source;
    popup.Placement = PlacementMode.Bottom;
    TextBlock txtblock = new TextBlock();
    txtblock.Background = Brushes.LightBlue;
    txtblock.Foreground = Brushes.Blue;
    txtblock.Text = "this is the error message";
    popup.Child = txtblock;

    Binding is_open_binding = new Binding("(Validation.HasError)");
    is_open_binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
    is_open_binding.Source = source;
    is_open_binding.Mode = BindingMode.OneWay;
    is_open_binding.NotifyOnValidationError = true;
    is_open_binding.ValidatesOnExceptions = true;
    is_open_binding.Converter = new TempValueConverter();
    popup.SetBinding(Popup.IsOpenProperty, is_open_binding);
+1  A: 

Just did a simple test and it worked fine. Here is my XAML:

<Window x:Name="_root" x:Class="WpfApplication1.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>
     <TextBox x:Name="_textBox">
      <TextBox.Text>
       <Binding Path="Text" ElementName="_root" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
         <ExceptionValidationRule/>
        </Binding.ValidationRules>
       </Binding>
      </TextBox.Text>
     </TextBox>
     <!--<Popup x:Name="_popup" IsOpen="{Binding (Validation.HasError), ElementName=_textBox, Mode=OneWay}">-->
     <Popup x:Name="_popup">
      <Border BorderThickness="1" BorderBrush="Black" Background="White">
       <TextBlock>Here I am.</TextBlock>
      </Border>
     </Popup>
    </StackPanel>
</Window>

And here is the code-behind:

using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Data;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
     public string Text
     {
      get { return "Text"; }
      set { if (value != "Text") throw new InvalidOperationException("Bla"); }
     }

     public Window1()
     {
      InitializeComponent();

      var binding = new Binding("(Validation.HasError)");
      binding.Source = _textBox;
      binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
      binding.Mode = BindingMode.OneWay;
      binding.NotifyOnValidationError = true;
      binding.ValidatesOnExceptions = true;
      //binding.Converter = new TempValueConverter();
      _popup.SetBinding(Popup.IsOpenProperty, binding);
     }

     private sealed class TempValueConverter : IValueConverter
     {
      #region IValueConverter Members

      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
       throw new NotImplementedException();
      }

      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
       throw new NotImplementedException();
      }

      #endregion
     }
    }
}

HTH, Kent

Kent Boogaart
A: 

I also made a simple solution and copied in your code and it ran just fine. Kent had his Popup declared in XAML, but I used your exact code for creating the Popup and setting the binding, so that difference shouldn't be the cause of the problem for you.

I am wondering if you could post where your source variable is coming from. You don't show that and I am wondering if it is what you think it is.

Another thing you might posibly try is keeping a reference to the popup around incase it is being garbage collected. I believe this could be possible as if I recall correctly the binding uses a week event handler for change notification so their wouldn't be any lasting link to the Popup instance. I think this is unlikely however, but might be worth a shot.

FYI the code I used to test this is as follows.

XAML file:

<Window x:Class="PopupOpenBindingTest.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">
<Grid>
    <TextBox Height="23"
             Margin="54,12,104,0"
             Name="textBox1"
             VerticalAlignment="Top"
             Text="{Binding Text, ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged}" />
</Grid></Window>

Code-behind.

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

        DataContext = new DataObjectTest();
        this.Loaded += new RoutedEventHandler(Window1_Loaded);
    }

    void Window1_Loaded(object sender, RoutedEventArgs e)
    {
        TextBox source = textBox1;

        Popup popup = new Popup(); 
        popup.Name = "somepopup";
        popup.PlacementTarget = source; 
        popup.Placement = PlacementMode.Bottom; 
        TextBlock txtblock = new TextBlock(); 
        txtblock.Background = Brushes.LightBlue; 
        txtblock.Foreground = Brushes.Blue; 
        txtblock.Text = "this is the error message"; 
        popup.Child = txtblock;
        Binding is_open_binding = new Binding("(Validation.HasError)");// { Path = new PropertyPath(Validation.HasErrorProperty) }; 
        is_open_binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; 
        is_open_binding.Source = source; 
        is_open_binding.Mode = BindingMode.OneWay; 
        is_open_binding.NotifyOnValidationError = true; 
        is_open_binding.ValidatesOnExceptions = true; 
        //is_open_binding.Converter = new TempValueConverter(); 
        popup.SetBinding(Popup.IsOpenProperty, is_open_binding);
    }

    public class DataObjectTest
    {
        private string _text = string.Empty;

        public string Text
        {
            get { return _text; }
            set
            {
                if (value.Length > 5)
                    throw new InvalidOperationException("Blah blah blah");

                _text = value;
            }
        }
    }
Caleb Vear