PropertyStatus in TextBox Style

PropertyStatus in TextBox Style

Old forum URL: forums.lhotka.net/forums/t/8964.aspx


Russ posted on Thursday, May 20, 2010

I'm trying to create a WPF textbox style that incorporates PropertyStatus. This would have the benefit of not having to code the propertystatus control after every textbox on every form. I am unable to get it to work. I'm having difficulty with the template binding (I think). Here is my style source:

<!-- cslatextbox style -->

<Style x:Key="CslaTextBoxStyle" TargetType="{x:Type TextBox}">

  <Setter Property="Margin" Value="0,1,0,1"/>

  <Setter Property="Validation.ErrorTemplate">

    <Setter.Value>

      <ControlTemplate>

        <Grid>

          <Grid.ColumnDefinitions>

            <ColumnDefinition Width="*"/>

            <ColumnDefinition Width="Auto"/>

          </Grid.ColumnDefinitions>

          <AdornedElementPlaceholder Grid.Column="0" />

          <csla:PropertyStatus Grid.Column="1"

              Property="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Text}"

              TargetControl="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>

        </Grid>

      </ControlTemplate>

    </Setter.Value>

  </Setter>

  <Style.Triggers>

    <Trigger Property="IsReadOnly" Value="true">

      <Setter Property="Background" Value="lightyellow"/>

    </Trigger>

  </Style.Triggers>

</Style>

 

Here is the typical textbox Xaml:

<TextBox Grid.Column="1" Grid.Row="0" Name="CodeTextBox"

    Text="{Binding Code, Converter={StaticResource IdentityConverter}, ValidatesOnDataErrors=True,

    UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource CslaTextBoxStyle}"/>

 

Can anyone suggest a solution or explain why this can't be done.

Thanks

Russ

 

 

dagware replied on Sunday, May 23, 2010

First off, I know so little about all of this (I'm just learning WPF) that anything I say is probably wrong. But that's never stopped me before!

I think your PropertyStatus' Property binding should use TemplateBinding, not Binding. The way you have it now, it's bound to the contents of the Text property, not the Text binding source.

But I still can't figure out how to get the TargetControl binding to work correctly.

Dan

dagware replied on Sunday, May 23, 2010

I was assuming there was an error in the TargetControl binding, because VS said it was missing a Path. Turns out this is a known bug, and can be ignored, or you can fix it by adding "Path=.".

Dan

Russ replied on Tuesday, May 25, 2010

Thanks for the ideas dagware. Unfortunately I am still unable to find the correct syntax to get this to work.

Russ replied on Friday, May 28, 2010

I have given up on using a style and I am now attempting to create a CslaTextBox UserControl. The goal is to replace several thousand of these:

<TextBlock Grid.Column="0" Grid.Row="0" Name="CodeTextBlock" Text="Code:" Style="{StaticResource TextBlockStyle}" />

<TextBox Grid.Column="1" Grid.Row="0" Name="CodeTextBox"

         Text="{Binding Code, Converter={StaticResource IdentityConverter}, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"

         Style="{StaticResource TextBoxStyle}"

         IsReadOnly="{Binding ElementName=CodePropertyStatus, Path=CanWrite, Converter={StaticResource invertBoolConverter}}"/>

<csla:PropertyStatus Grid.Column="2" Grid.Row="0" x:Name="CodePropertyStatus" Property="{Binding Path=Code}"/>

 

with these:

<TextBlock Grid.Column="0" Grid.Row="0" Name="CodeTextBlock" Text="Code:" Style="{StaticResource TextBlockStyle}" />

<controls:CslaTextBox Grid.Column="1" Grid.Row="0" x:Name="CodeTextBox" Text="{Binding Code}" />

 

The CslaTextBox UserControl will simply have a TextBox and a PropertyStatus control. Here is the XAML:

<UserControl x:Class="LossTrack.Controls.CslaTextBox" x:Name="cslaTextBox"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  xmlns:csla="clr-namespace:Csla.Xaml;assembly=Csla.Xaml"

  mc:Ignorable="d"

  d:DesignHeight="23" d:DesignWidth="150">

 

  <UserControl.Resources>

    <csla:IdentityConverter x:Key="IdentityConverter" />

  </UserControl.Resources>

 

  <Grid x:Name="LayoutRoot">

    <Grid.ColumnDefinitions>

      <ColumnDefinition Width="*"/>

      <ColumnDefinition Width="Auto"/>

    </Grid.ColumnDefinitions>

   

    <TextBox Grid.Column="0" x:Name="textBox" IsEnabled="{Binding ElementName=propertyStatus, Path=CanWrite}" Margin="0,1,2,1"

             Text="{Binding ElementName=cslaTextBox, Path=Text, Converter={StaticResource IdentityConverter}, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>

   

    <csla:PropertyStatus Grid.Column="1" x:Name="propertyStatus"

                         Property="{Binding ElementName=cslaTextBox, Path=Text}"/>

 

  </Grid>

</UserControl>

 

Here is the CslaUserControl code behind:

using System;

using System.Windows;

using System.Windows.Controls;

 

namespace LossTrack.Controls

{

    public partial class CslaTextBox : UserControl

    {

        public CslaTextBox()

        {

            InitializeComponent();

        }

 

        public static readonly DependencyProperty TextProperty =

            DependencyProperty.Register("Text",

            typeof(string), typeof(CslaTextBox),

            new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(textChangedCallBack)));

 

        public string Text

        {

            get { return (string)GetValue(TextProperty); }

            set {   SetValue(TextProperty, value); }

        }

 

        static void textChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)

        {

            CslaTextBox cslaTextBox = (CslaTextBox)property;

            cslaTextBox.textBox.Text = (string)args.NewValue;

        }

    }

}

 

The TextBox Text binding appears to operate correctly in all respects. The problem is getting the correct XAML syntax for the clsa:PropertyStatus Property property.

Does anyone have any suggestions?

Thanks in advance

Russ.

RockfordLhotka replied on Friday, May 28, 2010

PropertyStatus is a little special, in that it needs to get a reference to the actual underlying business object so it can add a handler for the object's PropertyChanged event. To do this, it takes the binding expression and uses it to get the underlying source object.

My guess is that what's happening here is that the underlying source object is the usercontrol, not the business object - but without using the debugger to walk through the PropertyStatus code it is hard to say for sure. That level of data binding infrastructure gets somewhat complex.

There is a GetRealSource() method in PropertyStatus that walks back through the dot notation of the Path to find the source. I'm guessing that code doesn't handle whatever is different about being inside a usercontrol.

Turntwo replied on Friday, October 15, 2010

If anyone has figured this out, I'm trying to do the same thing, and having similar issues.  It would make things so much simpler (and cleaner) if this could work.

Turntwo replied on Friday, October 15, 2010

OK, so I think I figured out a way to do it (haven't cleaned it up yet, but it appears to work).  Basically stole the technique from PropertyStatus.

The key part here is the SetSource method, which Binds the PropertyStatus.Property to the BOTextBox.Property binding - which then allows PropertyStatus to do it's thing.

The XAML:

<UserControl x:Class="JefBar.Core.WPF.BOTextBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 xmlns:corewpf="clr-namespace:JefBar.Core.WPF"
 xmlns:csla="clr-namespace:Csla.Xaml;assembly=Csla.Xaml"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
 x:Name="boTextBox">
    <StackPanel
Orientation="Horizontal"
>
<TextBox
Name="patientNumber"
Text="{Binding ElementName=boTextBox, Path=Property, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"
IsEnabled="{Binding ElementName=propertyStatus, Path=CanWrite}"
Visibility="{Binding ElementName=propertyStatus, Path=CanRead, Converter={corewpf:BooleanToVisibilityConverter}}"
MinWidth="75" 
/>
<csla:PropertyStatus
x:Name="propertyStatus"
/>

</StackPanel>
</UserControl>

The Code Behind (interesting part):
using System.Windows;
using System.Windows.Data;
using Csla.Xaml;

namespace JefBar.Core.WPF
{
    /// <summary>
    /// Interaction logic for BOTextBox.xaml
    /// </summary>
    public partial class BOTextBox
    {
        /// <summary>
        /// Gets or sets the source business
        /// property to which this control is bound.
        /// </summary>
        public static readonly DependencyProperty PropertyProperty = DependencyProperty.Register(
          "Property",
          typeof(object),
          typeof(BOTextBox),
           new PropertyMetadata(new object(), (o, e) => ((BOTextBox) o).SetSource())
          );

        /// <summary>
        /// Gets or sets the source business
        /// property to which this control is bound.
        /// </summary>
        public object Property
        {
            get { return GetValue(PropertyProperty); }
            set { SetValue(PropertyProperty, value); }
        }

        /// <summary>
        /// Sets the source binding and updates status.
        /// </summary>
        private BindingExpression oldBinding;
        protected virtual void SetSource()
        {
            var binding = GetBindingBLOCKED EXPRESSION;
        }
    }
}


cds replied on Friday, October 15, 2010

This looks interesting - I'd like to see the full code - but it seems it hasn't come out properly. (See the BLOCKED EXPRESSION at the end).

Turntwo replied on Saturday, May 14, 2011

Here is the source as an attachment, updated quite a bit since the original post (various templates and properties to control behavior).

Copyright (c) Marimer LLC