Validation Error Style in WPF, similar to Silverlight
Asked Answered
Z

4

64

By default, the Validation.ErrorTemplate in WPF is just a small red border without any ToolTip.

In Silverlight 4, the validation error is nicely styled out-of-the-box.

Here is a comparison of a validation error occuring in Silverlight 4 and WPF

Silverlight 4
enter image description here
WPF
enter image description here

Notice the really flat, boring look of the WPF version compared to the, in my opinion, great look in Silverlight.

Does any similar validation styles/templates exist in the WPF Framework or has anybody created nicely styled validation templates like the Silverlight version above? Or will I have to create them from scratch?

If anybody wants to try it out, the validation error above can be reproduced with the following code, works for both Silverlight and WPF

MainWindow/MainPage.xaml

<StackPanel Orientation="Horizontal" Margin="10" VerticalAlignment="Top">
    <TextBox Text="{Binding Path=TextProperty, Mode=TwoWay, ValidatesOnExceptions=True}"/>
    <Button Content="Tab To Me..." Margin="20,0,0,0"/>
</StackPanel>

MainWindow/MainPage.xaml.cs

public MainWindow/MainPage()
{
    InitializeComponent();
    this.DataContext = this;
}

private string _textProperty;
public string TextProperty
{
    get { return _textProperty; }
    set
    {
        if (value.Length > 5)
        {
            throw new Exception("Too many characters");
        }
        _textProperty = value;
    }
}
Zoophobia answered 15/9, 2011 at 16:27 Comment(2)
I desperately want to correct the grammar error in "To many characters", but it's inside an image! Curses! :PHeadcheese
@djacobson: Ouch, "too" many grammar errors here :) I'll fix itZoophobia
Z
114

I studied the Silverlight version of the Validation Error Template and created a WPF version of it which looks like this

enter image description here
Added an animated GIF at the bottom of the post but after I finished it I noticed that it might be annoying because of the moving mouse in it. Let me know if I should remove it.. :)

I used a MultiBinding with a BooleanOrConverter to show the "tooltip-error" when the TextBox has Keyboard focus or the Mouse is over the upper right corner. For the fade-in animation I used a DoubleAnimation for the Opacity and a ThicknessAnimation with a BackEase/EaseOut EasingFunction for the Margin

Useable like this

<TextBox Validation.ErrorTemplate="{StaticResource errorTemplateSilverlightStyle}" />

errorTemplateSilverlightStyle

<ControlTemplate x:Key="errorTemplateSilverlightStyle">
    <StackPanel Orientation="Horizontal">
        <Border BorderThickness="1" BorderBrush="#FFdc000c" CornerRadius="0.7"
                VerticalAlignment="Top">
            <Grid>
                <Polygon x:Name="toolTipCorner"
                         Grid.ZIndex="2"
                         Margin="-1"
                         Points="6,6 6,0 0,0" 
                         Fill="#FFdc000c" 
                         HorizontalAlignment="Right" 
                         VerticalAlignment="Top"
                         IsHitTestVisible="True"/>
                <Polyline Grid.ZIndex="3"
                          Points="7,7 0,0" Margin="-1" HorizontalAlignment="Right" 
                          StrokeThickness="1.5"
                          StrokeEndLineCap="Round"
                          StrokeStartLineCap="Round"
                          Stroke="White"
                          VerticalAlignment="Top"
                          IsHitTestVisible="True"/>
                <AdornedElementPlaceholder x:Name="adorner"/>
            </Grid>
        </Border>
        <Border x:Name="errorBorder" Background="#FFdc000c" Margin="1,0,0,0"
                Opacity="0" CornerRadius="1.5"
                IsHitTestVisible="False"
                MinHeight="24" MaxWidth="267">
            <Border.Effect>
                <DropShadowEffect ShadowDepth="2.25" 
                                  Color="Black" 
                                  Opacity="0.4"
                                  Direction="315"
                                  BlurRadius="4"/>
            </Border.Effect>
            <TextBlock Text="{Binding ElementName=adorner,
                                      Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
                       Foreground="White" Margin="8,3,8,3" TextWrapping="Wrap"/>
        </Border>
    </StackPanel>
    <ControlTemplate.Triggers>
        <DataTrigger Value="True">
            <DataTrigger.Binding>
                <MultiBinding Converter="{StaticResource BooleanOrConverter}">
                    <Binding ElementName="adorner" Path="AdornedElement.IsKeyboardFocused" />
                    <Binding ElementName="toolTipCorner" Path="IsMouseOver"/>
                </MultiBinding>
            </DataTrigger.Binding>
            <DataTrigger.EnterActions>
                <BeginStoryboard x:Name="fadeInStoryboard">
                    <Storyboard>
                        <DoubleAnimation Duration="00:00:00.15"
                                         Storyboard.TargetName="errorBorder"
                                         Storyboard.TargetProperty="Opacity"
                                         To="1"/>
                        <ThicknessAnimation Duration="00:00:00.15"
                                            Storyboard.TargetName="errorBorder"
                                            Storyboard.TargetProperty="Margin"
                                            FillBehavior="HoldEnd"
                                            From="1,0,0,0"
                                            To="5,0,0,0">
                            <ThicknessAnimation.EasingFunction>
                                <BackEase EasingMode="EaseOut" Amplitude="2"/>
                            </ThicknessAnimation.EasingFunction>
                        </ThicknessAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <StopStoryboard BeginStoryboardName="fadeInStoryboard"/>
                <BeginStoryboard x:Name="fadeOutStoryBoard">
                    <Storyboard>
                        <DoubleAnimation Duration="00:00:00"
                                         Storyboard.TargetName="errorBorder"
                                         Storyboard.TargetProperty="Opacity"
                                         To="0"/>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.ExitActions>
        </DataTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

BooleanOrConverter

public class BooleanOrConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        foreach (object value in values)
        {
            if ((bool)value == true)
            {
                return true;
            }
        }
        return false;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

enter image description here

Zoophobia answered 15/9, 2011 at 21:39 Comment(15)
+1 Nice Work! I ended up putting this in a style so it could be used across the board on all of my controls.Ferraro
One thing to note is that the errorBorder's Visibility is always set to Visible and not collapsed when the mouse is not over or keyboard is not focused. I had a button to the right of a textbox and I could never click it when there was a validation error, even though the errorBorder's opacity was set to 0. To fix this I added the following in the comment below:Ferraro
<ObjectAnimationUsingKeyFrames Duration="00:00:00" Storyboard.TargetName="errorBorder" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Visibility.Collapsed}"/> </ObjectAnimationUsingKeyFrames>Ferraro
@Brent: True, I've never noticed it myself but it was easy enough to reproduce :) I solved it by adding IsHitTestVisible="False" to the errorBorder but your addition will work as well. Thanks for the updateZoophobia
I have another opportunity... if the control is at the edge of the window, the validation error is cut off. Do you have any suggestions on how to make this respect the window and display the validation error underneath the control instead of to the right when there is not enough room between the control and the window's edge?Ferraro
@Brent: The same thing has been bugging me. Instead of displaying it below I would like to display it outside of the window. I haven't had any time to look into it yet but when I do I'll update here. As for displaying it below, try changing the StackPanels orientation from Horizontal to Vertical. I haven't tried it myself thoughZoophobia
If you are purist and want to avoid binding exceptions etc use this to avoid lame errors Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"Pitiless
@Meleak: awesome job, i am using this template in my application, but i should note though, that it has caused an exception in the BooleanOrConverter complaining that not all values are really bools when i used it inside my data grid. i am still tracking the cause.Conflict
@Parhs: Just what I was looking for to get rid of the exceptions. Big thanks.Powerful
The BooleanOrConverter will throw an exception if any of the values passed in is not convertable to bool, e.g. the value passed in is DependencyProperty.UnsetValue. This one will do the trick though; public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { return values.OfType<bool>().Any(value => value); }Neckwear
It could be improved by using a popup : <Popup x:Name="errorPopup" PopupAnimation="Fade" AllowsTransparency="True" Placement="Right"> and a trigger in order to open the popup when the user has the mouse over the tooltip. <Setter TargetName="errorPopup" Property="IsOpen" Value="True"/>Quintan
@FredrikHedblad i get a warning using this template System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'ElementName=adorner'. BindingExpression:Path=AdornedElement.IsKeyboardFocused; DataItem=null; target element is 'Control' (Name=''); target property is 'NoTarget' (type 'Object'), Is this warning OK or i am having some problem in the code?Doctorate
@FredrikHedblad, very nice but the error message sometimes appear outside the viewable area when the control is on extreme right. How to control that too from within control template, displaying error message to bottom in this case only?Eckhardt
It has only a little problem that the red boxes are still visible when there is some popup window over it. It is discribed here: #1471951 But is it possible to treat it somehow in that errorTemplateSilverlightStyle style?Wisniewski
Impressive work, very nice just one question, when typing something on the textbox it is so slow. Is that normal ?Mars
C
38

This answer merely expands on Fredrik Hedblad's excellent answer. Being new to WPF and XAML, Fredrik's answer served as a springboard for defining how I wanted validation errors to be displayed in my application. While the XAML below works for me, it is a work in progress. I have not fully tested it, and I will readily admit that I cannot fully explain every tag. With those caveats, I hope this proves useful to others.

While the animated TextBlock is a fine approach, it has two shortcomings that I wanted to address.

  1. First, as Brent's comment noted, the text is constrained by the owning window's borders such that if the invalid control is at the edge of the window, the text is cut off. Fredrik's suggested solution was to have it displayed "outside the window." That makes sense to me.
  2. Second, showing the TextBlock to the right of the invalid control is not always optimal. For example, say the TextBlock is used to specify a particular file to open and that there is a Browse button to its right. If the user types in a non-existent file, the error TextBlock will cover the Browse button and potentially prevent the user from clicking it to correct the mistake. What makes sense to me is to have the error message displayed diagonally up and to the right of the invalid control. This accomplishes two things. First, it avoids hiding any companion controls to the right of the invalid control. It also has the visual effect that the toolTipCorner is pointing toward the error message.

Here is the dialog around which I've done my development.

Basic Dialog

As you can see, there are two TextBox controls that need to be validated. Both are relatively close to the right edge of the window, so long error messages would likely be cropped. And notice that the second TextBox has a Browse button that I don't want hidden in the event of an error.

So here's what a validation error looks like using my implementation.

enter image description here

Functionally, it is very similar to Fredrik's implementation. If the TextBox has focus, the error will be visible. Once it loses focus, the error disappears. If the user hovers the mouse over the toolTipCorner, the error will appear regardless of whether the TextBox has focus or not. There are a few cosmetic changes as well, such as the toolTipCorner being 50% larger (9 pixels vs. 6 pixels).

The obvious difference, of course, is that my implementation uses a Popup to display the error. This solves the first shortcoming because the Popup displays its contents in its own window, so it is not constrained by the dialog's borders. However, using a Popup did present a couple challenges to overcome.

  1. It appears from testing and online discussions that the Popup is considered a topmost window. So even when my application was hidden by another application, the Popup was still visible. This was less-than-desirable behavior.
  2. The other gotcha was that if the user happened to move or resize the dialog box while the Popup was visible, the Popup did not reposition itself to maintain its position relative to the invalid control.

Fortunately, both of these challenges have been addressed.

Here's the code. Comments and refinements are welcome!


  • File: ErrorTemplateSilverlightStyle.xaml
  • Namespace: MyApp.Application.UI.Templates
  • Assembly: MyApp.Application.UI.dll

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
  xmlns:behaviors="clr-namespace:MyApp.Application.UI.Behaviors">

  <ControlTemplate x:Key="ErrorTemplateSilverlightStyle">
    <StackPanel Orientation="Horizontal">
      <!-- Defines TextBox outline border and the ToolTipCorner -->
      <Border x:Name="border" BorderThickness="1.25"
                              BorderBrush="#FFDC000C">
        <Grid>
          <Polygon x:Name="toolTipCorner"
                   Grid.ZIndex="2"
                   Margin="-1"
                   Points="9,9 9,0 0,0"
                   Fill="#FFDC000C"
                   HorizontalAlignment="Right"
                   VerticalAlignment="Top"
                   IsHitTestVisible="True"/>
          <Polyline Grid.ZIndex="3"
                    Points="10,10 0,0"
                    Margin="-1"
                    HorizontalAlignment="Right"
                    StrokeThickness="1.5"
                    StrokeEndLineCap="Round"
                    StrokeStartLineCap="Round"
                    Stroke="White"
                    VerticalAlignment="Top"
                    IsHitTestVisible="True"/>
          <AdornedElementPlaceholder x:Name="adorner"/>
        </Grid>
      </Border>
      <!-- Defines the Popup -->
      <Popup x:Name="placard"
             AllowsTransparency="True"
             PopupAnimation="Fade"
             Placement="Top"
             PlacementTarget="{Binding ElementName=toolTipCorner}"
             PlacementRectangle="10,-1,0,0">
        <!-- Used to reposition Popup when dialog moves or resizes -->
        <i:Interaction.Behaviors>
          <behaviors:RepositionPopupBehavior/>
        </i:Interaction.Behaviors>
        <Popup.Style>
          <Style TargetType="{x:Type Popup}">
            <Style.Triggers>
              <!-- Shows Popup when TextBox has focus -->
              <DataTrigger Binding="{Binding ElementName=adorner, Path=AdornedElement.IsFocused}"
                           Value="True">
                <Setter Property="IsOpen" Value="True"/>
              </DataTrigger>
              <!-- Shows Popup when mouse hovers over ToolTipCorner -->
              <DataTrigger Binding="{Binding ElementName=toolTipCorner, Path=IsMouseOver}"
                           Value="True">
                <Setter Property="IsOpen" Value="True"/>
              </DataTrigger>
              <!-- Hides Popup when window is no longer active -->
              <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=IsActive}"
                           Value="False">
                <Setter Property="IsOpen" Value="False"/>
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </Popup.Style>
        <Border x:Name="errorBorder"
                Background="#FFDC000C"
                Margin="0,0,8,8"
                Opacity="1"
                CornerRadius="4"
                IsHitTestVisible="False"
                MinHeight="24"
                MaxWidth="267">
          <Border.Effect>
            <DropShadowEffect ShadowDepth="4"
                              Color="Black"
                              Opacity="0.6"
                              Direction="315"
                              BlurRadius="4"/>
          </Border.Effect>
          <TextBlock Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"
                     Foreground="White"
                     Margin="8,3,8,3"
                     TextWrapping="Wrap"/>
        </Border>
      </Popup>
    </StackPanel>
  </ControlTemplate>

</ResourceDictionary>


  • File: RepositionPopupBehavior.cs
  • Namespace: MyApp.Application.UI.Behaviors
  • Assembly: MyApp.Application.UI.dll

(NOTE: THIS REQUIRES THE EXPRESSION BLEND 4 System.Windows.Interactivity ASSEMBLY)

using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Interactivity;

namespace MyApp.Application.UI.Behaviors
{
    /// <summary>
    /// Defines the reposition behavior of a <see cref="Popup"/> control when the window to which it is attached is moved or resized.
    /// </summary>
    /// <remarks>
    /// This solution was influenced by the answers provided by <see href="https://stackoverflow.com/users/262204/nathanaw">NathanAW</see> and
    /// <see href="https://stackoverflow.com/users/718325/jason">Jason</see> to
    /// <see href="https://mcmap.net/q/302931/-how-can-i-move-a-wpf-popup-when-its-anchor-element-moves">this</see> question.
    /// </remarks>
    public class RepositionPopupBehavior : Behavior<Popup>
    {
        #region Protected Methods

        /// <summary>
        /// Called after the behavior is attached to an <see cref="Behavior.AssociatedObject"/>.
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();
            var window = Window.GetWindow(AssociatedObject.PlacementTarget);
            if (window == null) { return; }
            window.LocationChanged += OnLocationChanged;
            window.SizeChanged     += OnSizeChanged;
            AssociatedObject.Loaded += AssociatedObject_Loaded;
        }

        void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
        {
            //AssociatedObject.HorizontalOffset = 7;
            //AssociatedObject.VerticalOffset = -AssociatedObject.Height;
        }

        /// <summary>
        /// Called when the behavior is being detached from its <see cref="Behavior.AssociatedObject"/>, but before it has actually occurred.
        /// </summary>
        protected override void OnDetaching()
        {
            base.OnDetaching();
            var window = Window.GetWindow(AssociatedObject.PlacementTarget);
            if (window == null) { return; }
            window.LocationChanged -= OnLocationChanged;
            window.SizeChanged     -= OnSizeChanged;
            AssociatedObject.Loaded -= AssociatedObject_Loaded;
        }

        #endregion Protected Methods

        #region Private Methods

        /// <summary>
        /// Handles the <see cref="Window.LocationChanged"/> routed event which occurs when the window's location changes.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An object that contains the event data.
        /// </param>
        private void OnLocationChanged(object sender, EventArgs e)
        {
            var offset = AssociatedObject.HorizontalOffset;
            AssociatedObject.HorizontalOffset = offset + 1;
            AssociatedObject.HorizontalOffset = offset;
        }

        /// <summary>
        /// Handles the <see cref="Window.SizeChanged"/> routed event which occurs when either then <see cref="Window.ActualHeight"/> or the
        /// <see cref="Window.ActualWidth"/> properties change value.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An object that contains the event data.
        /// </param>
        private void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            var offset = AssociatedObject.HorizontalOffset;
            AssociatedObject.HorizontalOffset = offset + 1;
            AssociatedObject.HorizontalOffset = offset;
        }

        #endregion Private Methods
    }
}


  • File: ResourceLibrary.xaml
  • Namespace: MyApp.Application.UI
  • Assembly: MyApp.Application.UI.dll

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>

        <!-- Styles -->
        ...

        <!-- Templates -->
        <ResourceDictionary Source="Templates/ErrorTemplateSilverlightStyle.xaml"/>

    </ResourceDictionary.MergedDictionaries>

    <!-- Converters -->
    ...

</ResourceDictionary>


  • File: App.xaml
  • Namespace: MyApp.Application
  • Assembly: MyApp.exe

<Application x:Class="MyApp.Application.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="Views\MainWindowView.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/MyApp.Application.UI;component/ResourceLibrary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>


  • File: NewProjectView.xaml
  • Namespace: MyApp.Application.Views
  • Assembly: MyApp.exe

<Window x:Class="MyApp.Application.Views.NewProjectView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:MyApp.Application.Views"
        xmlns:viewModels="clr-namespace:MyApp.Application.ViewModels"
        Title="New Project" Width="740" Height="480"
        WindowStartupLocation="CenterOwner">

  <!-- DATA CONTEXT -->
  <Window.DataContext>
    <viewModels:NewProjectViewModel/>
  </Window.DataContext>

  <!-- WINDOW GRID -->
  ...

  <Label x:Name="ProjectNameLabel"
         Grid.Column="0"
         Content="_Name:"
         Target="{Binding ElementName=ProjectNameTextBox}"/>
  <TextBox x:Name="ProjectNameTextBox"
           Grid.Column="2"
           Text="{Binding ProjectName,
                          Mode=TwoWay,
                          UpdateSourceTrigger=PropertyChanged,
                          ValidatesOnDataErrors=True}"
           Validation.ErrorTemplate="{StaticResource ErrorTemplateSilverlightStyle}"/>

  ...
</Window>
Callihan answered 5/12, 2013 at 7:56 Comment(2)
I like this but the RepositionPopupBehavior doesn't work with window state changes (Maximize/Restore). I thought that adding another event handler in the same way as for the LocationChanged and SizeChanged events would fix this but that doesn't work either :/Tedmund
Has anybody experienced an "off by one" error when changing the value in the text box? Say I have zero and that's the validation error the error box doesn't go until I type "11" (1 then 1) instead of "1".Limn
L
3

I have created my custom error adorner in one of the projects to show the error adorner just below my textbox with error message in it. You just need to set the property "Validation.ErrorTemplate" in your textbox default style which you can keep in your app resources so that it get applied to all the textboxes in your application.

Note: I have used some brushes here, replace it with your own set of brushes which you want for your adorner messgae. May be this can be of some help :

<Setter Property="Validation.ErrorTemplate">
              <Setter.Value>
                <ControlTemplate>
                  <StackPanel>
                    <!--TextBox Error template-->
                    <Canvas Panel.ZIndex="1099">
                      <DockPanel>
                        <Border BorderBrush="{DynamicResource HighlightRedBackgroundBrush}" BorderThickness="2" Padding="1" CornerRadius="3">
                          <AdornedElementPlaceholder x:Name="ErrorAdorner" />
                        </Border>
                      </DockPanel>
                      <Popup IsOpen="True" AllowsTransparency="True" Placement="Bottom" PlacementTarget="{Binding ElementName=ErrorAdorner}" StaysOpen="False">
                        <Border Canvas.Bottom="4"
                Canvas.Left="{Binding Path=AdornedElement.ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}"
                BorderBrush="{DynamicResource HighlightRedBackgroundBrush}"
                BorderThickness="1"
                Padding="4"
                CornerRadius="5"
                Background="{DynamicResource ErrorBackgroundBrush}">
                          <StackPanel Orientation="Horizontal">
                            <ContentPresenter Width="24" Height="24" Content="{DynamicResource ExclamationIcon}" />
                            <TextBlock TextWrapping="Wrap"
                   Margin="4"
                   MaxWidth="250"
                   Text="{Binding Path=AdornedElement.(Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}" />
                          </StackPanel>
                        </Border>
                      </Popup>
                    </Canvas>
                  </StackPanel>
                </ControlTemplate>
              </Setter.Value>
            </Setter>
Liegeman answered 15/9, 2011 at 18:36 Comment(4)
Our team really liked the Silverlight version of the error template so I recreated it for WPF. Thanks anyway!Zoophobia
Ok. Cool. That was just a sample template as per our project requirements.Liegeman
Liked your animation part in the template. :) +1 for your post.. Would suggest this for my project also..Liegeman
When the textbox losts the focus, then re-get the focus again, the popup does not show.Flaviaflavian
S
0

I ran into an issue with this when trying to apply it to a wpf project I'm working on. If you're having the following issue when you try to run the project:

"An exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll but was not handled in user code"

You need to create an instance of the booleanOrConverter class in your resources (within app.xaml):

<validators:BooleanOrConverter x:Key="myConverter" />

Also don't forget to add the namespace to the top of the file (in the application tag):

xmlns:validators="clr-namespace:ParcelRatesViewModel.Validators;assembly=ParcelRatesViewModel"

Symer answered 30/5, 2017 at 21:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.