WPF Data-binding Validation problem

  • Thread starter Thread starter Jim Hudson
  • Start date Start date
J

Jim Hudson

I have a little <UserControl> that gets hosted in a Canvas (so that I can
position it the way I want), constructed when the app starts, and then
displayed and hidden through appropriate settings of its Visibility property.

Inside the <UserControl>, I have a couple of <TextBox> controls, each of
which is subject to validation:

<TextBox x:Uid="DestinationTextBox"
x:Name="DestinationTextBox" Style="{StaticResource TextBoxStyle}"

Validation.ErrorTemplate="{StaticResource errorTemplate}"
MinWidth="200" >
<TextBox.Text>
<Binding x:Name="DestinationBinding"
x:Uid="DestinationBinding" Path="Rule.Destination"
NotifyOnSourceUpdated="True"
UpdateSourceTrigger="PropertyChanged" >
<Binding.ValidationRules>
<peme:DestinationValidationRule
x:Uid="DestinationValidationRule" x:Name="DestinationValidationRule" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>


I want the error template to be displayed whenever the bound data
("Rule.Destination") is invalid: both when the <UserControl> is initially
displayed, and also whenever the user enters bad data. To make that happen,
the containing application makes the <UserControl> visible, and then calls a
method of the <UserControl> that does the following:

// Getting data-binding to work the way I want it to is NOT
easy. First, we have to
// force the XAML objects to reload themselves from the bound
data. I can cause that
// to happen by clearing and then resetting the DataContext for
the XAML objects.
this.DataContext = null;
this.DataContext = this;

// THEN, I need to force the validation rules to be fired. That
only happens
// when the binding source (which is this.Rule.xxxx) gets
updated by the data-binding.
// Since the controls now have copies of the values in
this.Rule, I'll just force
// them to copy them back by calling UpdateSource(). The
validation will happen
// along the way.

this.DestinationTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();

MY PROBLEM:

EVERY time that the <UserControl> is made visible, the
BindingExpression.UpdateSource() method gets called and correctly sets the
Validation.HasErrors attached property to "true". I've verified this by
displaying that property in the UI. HOWEVER, THE FIRST TIME its displayed,
the error-template adorner is not shown. If I then change the Visibility of
the <UserControl> to Collapsed (to hide it) and then Visible (to re-display
it), then the error-template adorner appears.

What do I have to do to get the error-template adorner to appear EVERY time?
 
Hi Jim,

Thank you for using Microsoft Managed Newsgroup Service, my name is Zhi-Xin
Ye, I'm assigned to help you on this issue.

As I understand, you want the error template to be displayed when the
UserControl is initially displayed.

You can handle the Loaded event on the UserControl or on the Window, and
call the UpdateSource() method in the event handler. For example:

void UserControl1_Loaded(object sender, RoutedEventArgs e)
{

this.textBox1.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

Please try my suggestion and let me know the result.

Sincerely,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can

improve the support we provide to you. Please feel free to let my manager
know what you think of the level

of service provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to

http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the

community or a Microsoft Support Engineer within 2 business day is
acceptable. Please note that each follow

up response may take approximately 2 business days as the support
professional working with you may need

further investigation to reach the most efficient resolution. The offering
is not appropriate for situations

that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working

with a dedicated Microsoft Support Engineer by contacting Microsoft
Customer Support Services (CSS) at

http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Thanks for getting back to me, Zhi-Xin Ye.

The sequence of events, when my program runs, is this:

1) My UserControl is defined in the XAML, placed on a <Canvas>. Therefore,
it is constructed by the g.cs code for the <Page>. Its Visibility is
specified as "Collapsed" in the XAML code.
2) When the <Page> is displayed, the Load event for the UserControl is
fired. At that point, the DataContext for the UserControl is null, so the
binding for the TextBox does nothing. I ADDED THE CODE YOU SUGGESTED. IT
FIRED AT THIS POINT AND DID NOT FIX THE PROBLEM.
3) Later, as a result of the user operating another control on the <Page>,
the code-behind logic that I showed in my original message is fired: the
DataContext for the <UserControl> is set, the Visibility for the
<UserControl> is set to "Visible", and the UpdateSource() method is called
for the textbox's binding expression.
4) The FIRST time the <UserControl> is made visible, the adorners defined in
the error template are NOT displayed. However, the NEXT time (and all
subsequent times), the adorners ARE displayed.
5) At one point, I added an experimental TextBox to the UserControl, binding
the Text property of the experimental TextBox to the Validation.HasError
property of the other TextBox. Every time the UserControl was displayed,
that experimental TextBox contained "true" (the FIRST and all subsequent
times). So, I'm sure that the Validation is firing on that FIRST display,
and I'm sure that it's setting the property that triggers the error template.


Clearly, something happens after my code-behind logic executes and before
the UserControl is displayed for the first time. Whatever it is, that
"mystery event" has to occur before my error template adorners appear.
Perhaps some error template layer gets created? Whatever it is, I need to
force it to happen sooner in the processing. Do you have any idea what that
"mystery event" might be?
 
Hi Jim,

I can reproduce the problem with the steps, it seems that there's something
stops the error adorner from displaying for the first time. I will do more
research to see what happens.
However, an idea to fix the problem is using a timer to call the
UpdateSource() method for the textbox's binding expression right after
clicking the button. For example:

private void button1_Click(object sender, RoutedEventArgs e)
{
if (uc1.Visibility == Visibility.Collapsed)
uc1.Visibility = Visibility.Visible;
else if (uc1.Visibility == Visibility.Visible)
uc1.Visibility = Visibility.Collapsed;

System.Timers.Timer t = new System.Timers.Timer();
t.Interval = 1;
t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
t.Start();
}

void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.Dispatcher.Invoke(new UpdateDelegate(update));
System.Timers.Timer t = sender as System.Timers.Timer;
t.Stop();
}

public delegate void UpdateDelegate();

public void update()
{

uc1.textBox1.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

I'm doing more research on this problem and will come back to you as soon
as possible.

Sincerely,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can

improve the support we provide to you. Please feel free to let my manager
know what you think of the level

of service provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.

============================================================================
=

Does my last reply make sense to you? If you need further help, please feel
free to let me know, I will be happy to be of assistance.
 
Once again, Zhi-Xin Ye, thanks for getting back to me.

It turned out that I had to switch my little UserControls into modeless
dialog Windows. I create each Window when I need it, then set the data
binding, then Show() it. The user interacts with it, and the application.
Once they click the "OK" button in the dialog, I Close() the window.

What I found is that the following sequence makes the ErrorTemplate adorners
appear when they're supposed to, ALL the time:

1) Construct the Window
2) Set the Window.DataContext
3) Call Window.Show()
4) Call UpdateSource() for each BindingExpression that has a ValidationRule

Apparently, the Show() method performs the necessary side-effect to get my
ErrorTemplate adorners to appear. I'm all set - you can close this issue.
 
Back
Top