Setting and displaying a property in a WPF UserControl - wpf

Am i missing something here? I have created a usercontrol with a property and for arguments sake it has a text box in it.
<UserControl x:Class="Isd.Utility.SystemMonitorWpf.Bar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock x:Name="txtExpected" Grid.Column="0" Grid.ColumnSpan="2" Width="auto" Height="auto" FontSize="10" LayoutTransform="{StaticResource Rotate}" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Tahoma" Foreground="Red" Panel.ZIndex="100" Margin="5,5,5,5"/>
Then in the code behind i have
public partial class Bar : UserControl
{
private string _PropTest;
public string PropTest
{
get { return _PropTest; }
set { _PropTest = value; }
}
public Bar()
{
InitializeComponent();
txtExpected.Text = PropTest;
}
}
Then i drop the usercontrol into the xaml and set the property to a value
<local:Bar PropTest="test"></local:Bar>
In this example, when the usercontrol is displayed the text is showing as null, its like the property PropTest never got set. Am I missing something obvious here? Thanks in advance.

When used as an attribute like you have it, PropTest gets set after the constructor is called, so it doesn't get set when you apply the property to the text box.
You'd be better attaching an event to the property changing, or use the TextBox as the backing value for the property.

It's because the value of the attribute will never set on the Text-Property of the txtExpected-Control. At the time when the constructor is called, the property PropTest still null.
So, you have to change the implementation of your property:
public string PropTest
{
get { return txtExpected.Text; }
set { txtExptected.Text = value; }
}

You should use DependencyProperties, so you can bind your control properties via xaml
On your class declaration:
public static readonly DependencyProperty MyProperty = DependencyProperty.Register(
"MyProperty", //Property name
typeof(string), //Property type
typeof(MyControl), //Type of the dependency property provider
new PropertyMetadata(MyPropertyChanged));//Callback invoked on property value has changes
public string MyProperty
{
set
{
this.SetValue(MyProperty, value);
}
get
{
return (string)this.GetValue(MyProperty);
}
}
private static void MyPropertyChanged( object sender, DependencyPropertyChangedEventArgs args )
{
// update your control inner elements properties
}
Edited a few times because of typos :P

You don't appear to be doing anything in the setter of PropTest. It won't be set prior to construction, so it will be null when you do:
txtExpected.Text = PropTest;
Inside your constructor. If you do this:
public string PropTest
{
get { return _PropTest; }
set
{
_PropTest = value;
txtExpected.Text = PropTest;
}
}
It should work. It's not what I'd call an "ideal" way to do things though, you might want to take a look at Dependency Properties, INotifyPropertyChanged and Binding.

What happens when you add Text attribute like so:
<TextBlock x:Name="txtExpected" Text="{Binding PropTest}" />
and eliminate the line
txtExpected.Text = PropTest;
from the constructor?

Delegate value assignment in PropTest property to TextBox:
public string PropTest
{
get { return txtExpected.Text; }
set { txtExpected.Text = value; }
}

Related

Pass textbox from a page to Custom usercontrol using dependencyProperty

I have a Custom user control in a silver light Project.
I use it in other page and want to Pass textbox to Custom User control.
For this I create dependcy as below :
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("TextBoxControl", typeof(TextBox), typeof(SpellCheck), new PropertyMetadata(false));
public TextBox TextBoxControl
{
get { return (TextBox)GetValue(MyPropertyProperty); }
set
{
SetValue(MyPropertyProperty, value);
TextSpell = value;
}
}
Here TextSpell is a textbox.
And I use this property in a silver light page as below:
<TextBox x:Name="txtNote" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="400"/>
<myButton:SpellCheck x:Name="btnSpell" Grid.Row="3" TextBoxControl="txtNote" Grid.Column="1" Width="20" Height="20" Margin="403,0,0,0" HorizontalAlignment="Left"/>
But I give s me a error : "The Typeconvertor for Texbox dose not support converting from a string"
So How can I pass a text box in custom usercontrol.
Thanks,
Hitesh
You can not simply use the field name (x:Name) string of the TextBox as a value for your TextBoxControl property. Instead you may use an ElementName binding like this:
<myButton:SpellCheck TextBoxControl="{Binding ElementName=txtNote}" ... />
And there are more things wrong:
In the CLR wrappers of a dependency property, you should never call anything else than GetValue and SetValue. The explanation is given in the XAML Loading and Dependency Properties article on MSDN. Instead, you have to have a PropertyChangedCallback registered with the property metadata.
There is a naming convention for the static dependency property fields. They should be named like the property, with a trailing Property.
The default value has to match the property type. Your false value is not valid, and might be null instead. But as that is the default anyway, you should leave it out completely.
The declaration would now look like this:
public static readonly DependencyProperty TextBoxControlProperty =
DependencyProperty.Register(
"TextBoxControl", typeof(TextBox), typeof(SpellCheck),
new PropertyMetadata(TextBoxControlPropertyChanged));
public TextBox TextBoxControl
{
get { return (TextBox)GetValue(TextBoxControlProperty); }
set { SetValue(TextBoxControlProperty, value); }
}
private static void TextBoxControlPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var spellCheck = (SpellCheck)obj;
spellCheck.TextSpell = (TextBox)e.NewValue;
}

Usercontrol with a Dependency Property doesn't recognise changes

I have a custom class which a usercontrol has implemented as a dependency property in it's code behind.
public partial class HandControl
{
public HandControl()
{
InitializeComponent();
}
public Seat Seat
{
get
{
return (Seat)GetValue(SeatProperty);
}
set
{
SetValue(SeatProperty, value);
}
}
public static readonly DependencyProperty SeatProperty = DependencyProperty.Register("Seat", typeof(Seat), typeof(HandControl), new PropertyMetadata(null));
}
In my case I've bound the name property in that class to a label inside the usercontrols xaml.
<Label Content="{Binding Seat.Player.Name, RelativeSource={RelativeSource AncestorType={x:Type controls:HandControl}}}"/>
The view model of my window contains the property SeatTl and the xaml is binding to it:
public Seat SeatTr
{
get { return _seatTr; }
private set
{
_seatTr = value;
OnPropertyChanged();
}
}
<customControls:HandControl Grid.Row="1"
Grid.Column="3"
Seat="{Binding SeatTr}" />
However, when I change my class content (the name property) and manually raise OnPropertyChanged in my viewmodel (not the usercontrol), the label is not updated and still has the same content.
private void OnSeatChanged(Player player, SeatPosition seatPosition)
{
//... doing the changes ...\\
OnPropertyChanged("SeatTr");
}
Whats my problem? Anyone got a clue?
I think u should raise OnPropertyChanged for Seat.Player.Name property as It is being chaged.

Binding a Dynamic Application.Resource WPF - User Control

I have a custom class:
class CustomClass
{
public string Test { get; set; }
CustomClass()
{
this.Test = "";
}
}
I'm declaring this custom class on a Application.Resources like that:
<Application.Resources>
<local:CustomClass x:Key="myobj"/>
</Application.Resources>
This resource is the DataContext of a grid and the TextBox binds the Test property, like that:
<Grid DataContext="{DynamicResource myobj}">
<TextBox Text="{Binding Path=Test, Mode=TwoWay}"></TextBox>
</Grid>
Suddenly at run-time, I change the value of the resource
this.Resources["myobj"] = new CustomClass() { Test = "12456" };
I want the value referenced on TextBox be always the value of the object that is currently on "myobj" resource, and I want change automatically the value of the current object when the value of Text property of the TextBox is changed, because of this, I used the Mode=TwoWay, but it's not happening.
I used WPF Inspector and I saw when the resource value is changed, binds a new cleared object and not my created object
I'm new in WPF sorry my english and my unknowledge;
Regards,
EDIT 1
It works implementing the code posted by ethicallogics, thanks! But sorry if I wasn't clear before, when binds a new resource as below
this.Resources["myobj"] = new instance;
it works fine when it is called inside the same window that this resource was declared, unlike when I call this line inside a UserControl, it seems that the UserControl doesn't inherit the MainWindow Resources, how that really works ?
class CustomClass:INotifyPropertyChanged
{
private string _test;
public string Test
{
get
{
return _test;
}
set
{
_test = value;
Notify("Test");
}
}
CustomClass()
{
this.Test = "";
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void Notify(string propName)
{
if(PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs(propName);
}
}
Use this class .I hope this will help.

WPF: Nested DependencyProperties

I have an ObservableCollection of "Layouts" and a "SelectedLocation" DependencyProperty on a Window. The SelectedLocation has a property called "Layout", which is an object containing fields like "Name" etc. I'm trying to bind a combobox to the SelectedLayout but it's not working.
The following does not work, I've tried binding to SelectedItem instead to no avail. I believe it may be something to do with the fact that I'm binding to a subProperty of the SelectedLocation DependencyProperty (though this does implement INotifyPropertyChanged.
<ComboBox Grid.Row="2" Grid.Column="0" x:Name="cboLayout" ItemsSource="{Binding Layouts,ElementName=root}" SelectedValue="{Binding SelectedLocation.Layout.LayoutID,ElementName=root}" DisplayMemberPath="{Binding Name}" SelectedValuePath="LayoutID" />
However, the following works (Also bound to the "SelectedLocation" DP:
<TextBox Grid.Row="4" Grid.Column="1" x:Name="txtName" Text="{Binding SelectedLocation.Name,ElementName=root,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
What type property Layouts has? I suppose something like this this: IEnumerable<Layout>.
But you bind selected value to Layout.LayoutID. So you got situation, when combo box contains Layout objects, and you try to select it by Int identifier. Of course binding engine can't find any Int there.
I have no idea about details of your code, so one thing I could propose: try to reduce your binding expression: SelectedItem="{Binding SelectedLocation.Layout,ElementName=root}.
If no success, provide more code to help me understand what's going on.
====UPDATE====
As I've said, you are obviously doing something wrong. But I am not paranormalist and couldn't guess the reason of your fail (without your code). If you don't want to share your code, I decided to provide simple example in order to demonstrate that everything works. Have a look at code shown below and tell me what is different in your application.
Class Layout which exposes property LayoutId:
public class Layout
{
public Layout(string id)
{
this.LayoutId = id;
}
public string LayoutId
{
get;
private set;
}
public override string ToString()
{
return string.Format("layout #{0}", this.LayoutId);
}
}
Class SelectionLocation which has nested property Layout:
public class SelectedLocation : INotifyPropertyChanged
{
private Layout _layout;
public Layout Layout
{
get
{
return this._layout;
}
set
{
this._layout = value;
this.OnPropertyChanged("Layout");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var safeEvent = this.PropertyChanged;
if (safeEvent != null)
{
safeEvent(this, new PropertyChangedEventArgs(name));
}
}
}
And Window class with dependency properties (actually, in my example StartupView is UserControl, but it doesn't matter):
public partial class StartupView : UserControl
{
public StartupView()
{
InitializeComponent();
this.Layouts = new Layout[] { new Layout("AAA"), new Layout("BBB"), new Layout("CCC") };
this.SelectedLocation = new SelectedLocation();
this.SelectedLocation.Layout = this.Layouts.ElementAt(1);
}
public IEnumerable<Layout> Layouts
{
get
{
return (IEnumerable<Layout>)this.GetValue(StartupView.LayoutsProperty);
}
set
{
this.SetValue(StartupView.LayoutsProperty, value);
}
}
public static readonly DependencyProperty LayoutsProperty =
DependencyProperty.Register("Layouts",
typeof(IEnumerable<Layout>),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
public SelectedLocation SelectedLocation
{
get
{
return (SelectedLocation)this.GetValue(StartupView.SelectedLocationProperty);
}
set
{
this.SetValue(StartupView.SelectedLocationProperty, value);
}
}
public static readonly DependencyProperty SelectedLocationProperty =
DependencyProperty.Register("SelectedLocation",
typeof(SelectedLocation),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
}
XAML of StartupView:
<UserControl x:Class="Test.StartupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:HandyCopy"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Root">
<WrapPanel>
<ComboBox ItemsSource="{Binding Path=Layouts,ElementName=Root}"
SelectedItem="{Binding Path=SelectedLocation.Layout, ElementName=Root}"/>
</WrapPanel>
</UserControl>

Binding to a User Control's property, containing logic, with a property in View Model

I'm building an application using WPF and MVVM. I've come across a situation where I have a view containing a usercontrol (representing a Timer). This usercontrol has a property in it's code behind which performs some calculations before getting and setting data.
TimerControl.xaml.cs:
public DateTime? DateTimeValue
{
get
{
string hours = this.txtHours.Text;
string minutes = this.txtMinutes.Text;
string amPm = this.txtAmPm.Text;
if (!string.IsNullOrWhiteSpace(hours) && !string.IsNullOrWhiteSpace(minutes) && !string.IsNullOrWhiteSpace(amPm))
{
string value = string.Format("{0}:{1} {2}", this.txtHours.Text, this.txtMinutes.Text, this.txtAmPm.Text);
DateTime time = DateTime.Parse(value);
return time;
}
else
{
return null;
}
}
set
{
DateTime? time = value;
if (time.HasValue)
{
string timeString = time.Value.ToShortTimeString();
//9:54 AM
string[] values = timeString.Split(':', ' ');
if (values.Length == 3)
{
this.txtHours.Text = values[0];
this.txtMinutes.Text = values[1];
this.txtAmPm.Text = values[2];
}
}
}
}
Now I wanted to bind this property to a property present in view model of the view. Following is property in the VM:
public DateTime? StartTime
{
get
{
return _StartTime;
}
set
{
_StartTime = value;
RaisePropertyChanged("StartTime");
}
}
This is how I am performing binding in the xaml of View.
MyView.xaml:
<my:TimeControl Background="White" Grid.Column="1" Grid.Row="2" Margin="3" x:Name="StartTimeControl" DateTimeValue="{Binding StartTime}" Width="150" Height="26" HorizontalAlignment="Left">
But it is giving me an error that:
A 'Binding' cannot be set on the 'DateTimeValue' property of type 'TimeControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
I've been struggling for hours trying to figure out a way to make this binding work. I have even tried to make a dependency property in the TimeControl's code behind for the DateTimeValue property, which has resolved the above exception, but the binding still doesn't work. Whenever I access StartTime property in the VM's code behind, it is showing null. Although it should show a valid value by getting the DateTimeValue property.
Kindly suggest me a way to make this work. Thanks.
Your implementation of DateTimeValue property shown in this question is certainly wrong and leads to exception, because DateTimeValue should be dependency property.
But you mentioned that you have tried to use dependency property with no success. I suppose the reason is in collision of DataContexts and your XAML looks like this:
<UserControl x:Class="Test.SomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:Test"
Name="Root">
<WrapPanel>
<self:TimerControl Time="{Binding StartTime}"/>
</WrapPanel>
</UserControl>
This code doesn't work. Why? DataContext of TimerControl is inherited (or maybe you replace it at all), meanwhile when you address to StartTime you have in mind ViewModel as DataContext. So you should clearly point to correct DataContext:
<self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/>
===UPDATE===
The whole code of my Timer control (as you can see my Timer has textbox, when you input some text, textbox raises appropriate event, which we handle and set Time property):
public partial class Timer : UserControl
{
public Timer()
{
InitializeComponent();
}
public DateTime? Time
{
get
{
return (DateTime?)this.GetValue(Timer.TimeProperty);
}
set
{
this.SetValue(Timer.TimeProperty, value);
}
}
public static readonly DependencyProperty TimeProperty =
DependencyProperty.Register("Time",
typeof(DateTime?),
typeof(Timer),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (d, e) => { }));
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (DateTime.Now.Ticks % 2 == 0)
{
this.Time = DateTime.Now;
}
else
{
this.Time = null;
}
}
}
And XAML:
<UserControl x:Class="Test.Timer">
<Grid>
<TextBox TextChanged="TextBox_TextChanged"/>
</Grid>
</UserControl>
Usage of Time control in XAML:
<UserControl x:Class="Test.StartupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:Test"
Name="Root">
<WrapPanel>
<self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/>
</WrapPanel>
</UserControl>
Code behind of StartupView:
public StartupView()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
Property in ViewModel remains the same. During debugging setter of StartTime property fires every time when I change text in Timer.
What excatly do you want to do?
You can't bind to a standard property. If you want to bind you should use a dependency property.
public DateTime? DateTimeValue
{
get { return (DateTime?)GetValue(DateTimeValueProperty); }
set { SetValue(DateTimeValueProperty, value); }
}
// Using a DependencyProperty as the backing store for DateTimeValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DateTimeValueProperty =
DependencyProperty.Register("DateTimeValue", typeof(DateTime?), typeof(TimeControl), new UIPropertyMetadata(null));
Inside the UserControl:
<TextBox Text="{Binding DateTimeValue,RelativeSource={RelativeSource AncestorLevel=1, Mode=FindAncestor,AncestorType=UserControl}, Converter=...}" />
To bind directly to a DateTimeValue is not possible because there is no converter available for string->DateTime so you have to write an IValueConverter and specify this in your binding.
From outside of course you should be able to bind the value directly.

Resources