Attach property to usercontrol and updating it at design time - c#

How can I create a user control like the Textbox? For example when I change the Text property of a Textbox control the new text appears on the window that I am currently working with.
In my project I have a lot of places where the user has to enter information therefore I want to create a InputField user control. (that usercontrol consists of a label an a textbox with custom style)
Here is the xaml for my user control:
<UserControl x:Class="PDV.UserControls.InputField"
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"
mc:Ignorable="d" >
<Grid>
<StackPanel>
<Label Content="{Binding Path=LblContent}" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBox Height="23" Margin="5,-5,2,2" Name="textBox1" VerticalAlignment="Top" />
</StackPanel>
</Grid>
</UserControl>
and the code behind for that user control:
namespace PDV.UserControls
{
public partial class InputField : UserControl
{
public static DependencyProperty MessageProperty = DependencyProperty.Register(
"LblContent", typeof(string), typeof(UserControl));
public string LblContent{
get{
return (string)GetValue(MessageProperty);
}
set{
SetValue(MessageProperty, value);
}
}
//Constructor
public InputField(){
InitializeComponent();
this.DataContext = this;
}
}
}
so on my main window I will be able to use that user control by:
1) importing the namespace where that user control is:
xmlns:myCtrl ="clr-namespace:PDV.UserControls"
2) placing that control in that window:
<myCtrl:InputField LblContent="hello" Margin="0,0,483,0" Height="49" VerticalAlignment="Top"></myCtrl:InputField>
What do I have to do so that when I update LblContent="hello" it renders on the window? It will be nice for it to render at design time not just at runtime

I think that the second type of might be InputField
public static DependencyProperty MessageProperty = DependencyProperty.Register(
"LblContent", typeof(string), typeof(InputField));
I never try to set the DataContext in your way, eventually try to give a name at the usercontrol x:Name="Root" then change the binding like this: Content="{Binding Path=LblContent, ElementName=Root}"

Related

Using DependencyProperty to Display Placeholder Text on a TextBox (UWP)

I have a custom user control which consists only a TextBox, I want to be able to bing a Placeholder text to that Textbox. My UserControl code is like this
<UserControl
x:Class="Proj.Editors.EditTextControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MemberSuiteConsoleApp.Controls.Editors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<TextBox x:Name="txtbox" Width="300" PlaceholderText="{Binding ElementName=txtbox, Path=DataContext.PlaceholderText}"></TextBox>
</Grid>
public sealed partial class EditTextControl : UserControl
{
public EditTextControl()
{
this.InitializeComponent();
}
public string PlaceholderText
{
get { return (string)GetValue(PlaceholderTextProperty); }
set { SetValue(PlaceholderTextProperty, value); }
}
// Using a DependencyProperty as the backing store for PlaceholderText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PlaceholderTextProperty =
DependencyProperty.Register("PlaceholderText", typeof(string), typeof(EditTextControl), new PropertyMetadata(""));
}
And I am trying to use this UserControl in my page like this in my Page
<Grid>
<editors:EditTextControl PlaceholderText="My placeholder" Height="400"></editors:EditTextControl>
</Grid>
But for some reason, Placeholder text is not showing in Textbox, What I am missing here?
You can use x:Bind instead. I tried and works.
<TextBox x:Name="txtbox" Width="300" PlaceholderText="{x:Bind PlaceholderText, Mode=OneWay}"></TextBox>

TextBox Binding doesn't work when switching Tab in TabControl

I have a TextBox in a TabControl. If I edit the text in the box and then switch to another tab, the text is lost. If I change focus (via TAB key on keyboard) and then switch to another tab, the new text is set in my viewmodel.
Here is my code:
<Window x:Class="TabSwitchProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<TabControl ItemsSource="{Binding Pages}">
<TabControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding PageContent}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
public partial class MainWindow : Window
{
public ObservableCollection<PageViewModel> Pages
{
get { return (ObservableCollection<PageViewModel>)GetValue(PagesProperty); }
set { SetValue(PagesProperty, value); }
}
public static readonly DependencyProperty PagesProperty =
DependencyProperty.Register("Pages", typeof(ObservableCollection<PageViewModel>), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
Pages = new ObservableCollection<PageViewModel>();
Pages.Add(new PageViewModel());
Pages.Add(new PageViewModel());
DataContext = this;
}
}
public class PageViewModel : DependencyObject
{
public string PageContent
{
get { return (string)GetValue(PageContentProperty); }
set { SetValue(PageContentProperty, value); }
}
public static readonly DependencyProperty PageContentProperty =
DependencyProperty.Register("PageContent", typeof(string), typeof(PageViewModel), new PropertyMetadata(null));
}
How can I be sure to get the text updated in my viewmodel?
You may need to add UpdateSourceTrigger=LostFocus to the <TextBox Text="{Binding PageContent}" /> line.
Code should look like this
<TextBox Text="{Binding PageContent, UpdateSourceTrigger=LostFocus}" />
That should work.
You should set the UpdateSourceTrigger to PropertyChanged if you want your binding to update the target every time the value changes. By default the UpdateSourceTrigger for Text property of a TextBox is LostFocus, which updates the target only after the focus is lost.
<TextBox Text="{Binding PageContent, UpdateSourceTrigger=PropertyChanged}" />
The previously accepted answer, although it works, involves changing the binding behavior of the textbox to UpdatesourceTrigger=PropertyChanged. This may not be acceptable for some usages of textbox or other input-accepting controls.
A simple fix for this is to manually set focus to another element on your control (or the tabcontrol itself) in code-behind on SelectionChanged of your TabControl. That way the currently focused input element actually loses focus, triggering the binding:
<TabControl x:Name="MyTabControl" SelectionChanged="MyTabControl_OnSelectionChanged">
private void MyTabControl_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
MyTabControl.Focus();
}

Bind DependencyProperty of Usercontrol in ListBox

I need ListBox with my UserControl listed in it. My UserControl has TextBox. So I want to display property of List's subitem in UserControl's textBox. I have tried a lot of options with DataContext and ElementName - it just doesn`t work. I just stucked on it. The only way to make it work is to remove DataContext binding of UserControl to itself and change Item Property name so it matches to DependencyProperty name - but I need to reuse my control in different viewmodels with different entities so it is almost not possible to use the approach.
Interesting thing is that if I change my UserControl to Textbox and bind Text property of it - everything works. What the difference between Textbox and my UserControl?
So let me just show my code.
I have simplified the code to show only essential:
Control XAML:
<UserControl x:Class="TestControl.MyControl"
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"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBlock Text="{Binding Text}"/>
</Grid>
</UserControl>
Control CS:
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public string Text
{
get {
return (string)this.GetValue(TextProperty); }
set {
this.SetValue(TextProperty, value); }
}
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyControl), new propertyMetadata(""));
}
Window XAML:
<Window x:Class="TestControl.MainWindow"
Name="_windows"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestControl"
Title="MainWindow" Height="350" Width="525" >
<Grid Name="RootGrid">
<ListBox ItemsSource="{Binding ElementName=_windows, Path=MyList}">
<ItemsControl.ItemTemplate >
<DataTemplate >
<local:MyControl Text="{Binding Path=Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</Grid>
</Window>
Window CS:
public partial class MainWindow : Window
{
public MainWindow()
{
_list = new ObservableCollection<Item>();
_list.Add(new Item("Sam"));
_list.Add(new Item("App"));
_list.Add(new Item("H**"));
InitializeComponent();
}
private ObservableCollection<Item> _list;
public ObservableCollection<Item> MyList
{
get { return _list;}
set {}
}
}
public class Item
{
public Item(string name)
{
_name = name;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
This is a pretty big gotcha in XAML. The problem is that when you do this in the user control:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
You change its data context, so that in this line:
<local:MyControl Text="{Binding Path=Name}"/>
The runtime will now attempt to resolve "Name" on the instance of "MyControl", instead of on the inherited data context (ie, the view model). (Confirm this by checking the Output window -- you should see a binding error to that effect.)
You can get around this by, instead of setting the user control's data context that way, using a RelativeSource binding:
<UserControl x:Class="TestControl.MyControl"
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"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="200"
<Grid>
<TextBlock
Text="{Binding Text,RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
</Grid>
</UserControl>

WPF with MVVM and DataAnnotations, Validation Errors in a UserControl

I have a UserControl that will be reused throughout an application we are developing.
We are using a framework based on MVVMLight.
For the sake of simplicity lets say the user control contains only one textbox and exposes one dependency property named "Quantity". The textbox on the user control is databound to the dependency property "Quantity".
When the user control is used on a view, the "Quantity" dependency property of the usercontrol is databound to a property in a ViewModel. (This ViewModel is the datacontext of our view by way of the MVVMLight ViewModelLocator).
This all works great! The bindings work, properties are set like I would expect. All is well until it comes to validation.
We are using DataAnnotations to decorate our ViewModel properties. The ViewModel contains a custom implementation of INotifyDataErrorInfo. We have implemented custom styles for most input controls to show a red border around the control, and a message next to the control displaying the validation error message. All of this works great in a normal case (eg. Textbox on a View bound to a property in a view model).
When I attempt the same approach using this user control, what I end up with is a red border around the entire user control and no error indication on the actual textbox. It appears that the fact that there is an error is being reflected in the UI, but it's just not making it to the control I want it to.
I've searched on stackoverflow for this problem, of those questions with solutions, none seem to work for my situation.
My first guess is that because the actual textbox is bound directly to the dependency property itself and not the property on my view model, it is not being notified properly of the errors generated. Is there some way to propogate those errors generated in the viewmodel through the usercontrol and then to the textbox?
Any help or suggestions you can give would be great, thanks.
Here is the UserControl xaml.
<UserControl x:Class="SampleProject.UserControls.SampleControl"
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"
mc:Ignorable="d" x:Name="sampleControl"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="LayoutRoot" DataContext="{Binding ElementName=sampleControl}">
<TextBox Text="{Binding Path=Quantity, ValidatesOnDataErrors=True}" Width="100" Height="30" />
</Grid>
</UserControl>
The UserControl code behind.
public partial class SampleControl : UserControl
{
public SampleControl()
{
InitializeComponent();
}
public static readonly DependencyProperty QuantityProperty =
DependencyProperty.Register("Quantity", typeof(int?), typeof(SampleControl),
new FrameworkPropertyMetadata{DefaultValue=null, BindsTwoWayByDefault = true});
public int? Quantity
{
get { return (int?)GetValue(QuantityProperty); }
set { SetValue(QuantityProperty, value); }
}
}
Used on a view.
<userControls:SampleControl Grid.Row="1" Quantity="{Binding Path=Quantity, ValidatesOnDataErrors=True}" Height="60" Width="300"/>
The ViewModel property.
[Required(ErrorMessage = "Is Required")]
[Range(5, 10, ErrorMessage = "Must be greater than 5")]
public int? Quantity
{
get { return _quantity; }
set { Set(() => Quantity, ref _quantity, value); }
}
private int? _quantity;
(*Note, The Set method in the setter is just a helper method in the base viewmodel that sets the backing property and raises the PropertyChanged event for it.)
Try removing the DataContext from the UserControl. Instead of setting that, Bind directly from the TextBox to the actual property using a RelativeSource Binding:
<TextBox Text="{Binding Quantity, RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type YourControlNamespace:SampleControl,
ValidatesOnDataErrors=True}}}" Width="100" Height="30" />
UPDATE >>>
Failing that, as long as the view models that are bound to this property will always have a property of the same name to bind to, you can get this Binding to search through parents' DataContexts like this:
<TextBox Text="{Binding Quantity, RelativeSource={RelativeSource Mode=FindAncestor,
AncestorLevel=2, ValidatesOnDataErrors=True}}}" Width="100" Height="30" />
You will need to change the 2 to be the correct number of parent elements that the TextBox has before reaching the control with access to the correct property. For example, using a level of 2 means that the Framework will try to find a property named Quantity to Bind to in the DataContext of the TextBoxs parent's parent control. It is trickier getting this working with AncestorLevel though as I believe that 'hidden' elements like Grids are not included as parents.
You need to pick up the bindings set on the usercontrol and place them on the controls, there is no need to bind the usercontrol to it's own DataContext.
This can be done after the usercontrol is loaded.
To prevent a red border round the user control, remove the default error template:
Validation.ErrorTemplate="{x:Null}"
Sample user control XAML:
UserControl x:Class="DxUserControlValidation.MyUserControl"
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"
mc:Ignorable="d"
Validation.ErrorTemplate="{x:Null}"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Vertical">
<TextBlock Text="Value 1:" Margin="2"/>
<TextBox Name="txtBox1" Margin="2"/>
<TextBlock Text="Value 2:" Margin="2"/>
<TextBox Name="txtBox2" Margin="2"/>
</StackPanel>
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty Value1Property;
public static readonly DependencyProperty Value2Property;
static MyUserControl()
{
Value1Property = DependencyProperty.Register("Value1", typeof(string), typeof(MyUserControl), new FrameworkPropertyMetadata { DefaultValue = null, BindsTwoWayByDefault = true });
Value2Property = DependencyProperty.Register("Value2", typeof(string), typeof(MyUserControl), new FrameworkPropertyMetadata { DefaultValue = null, BindsTwoWayByDefault = true });
}
public MyUserControl()
{
InitializeComponent();
Loaded += (s, e) =>
{
Binding value1Binding = BindingOperations.GetBinding(this, Value1Property);
if (value1Binding != null) txtBox1.SetBinding(TextBox.TextProperty, value1Binding);
Binding value2Binding = BindingOperations.GetBinding(this, Value2Property);
if (value2Binding != null) txtBox2.SetBinding(TextBox.TextProperty, value2Binding);
};
}
public string Value1
{
get { return (string)GetValue(Value1Property); }
set { SetValue(Value1Property, value); }
}
public string Value2
{
get { return (string)GetValue(Value2Property); }
set { SetValue(Value2Property, value); }
}
}
If there is no binding, you van assign the value directly to the control:
if (value2Binding != null) txtBox2.SetBinding(TextBox.TextProperty, value2Binding);
else txtBox2.Text = Value2;

Custom usercontrol property binding failure silverlight

I have a custom usercontrol with DataContext="{Binding RelativeSource={RelativeSource self}}"
On the code behind i've made a dependency property like:
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) => { new Base().OnPropertyChanged("ElementName"); })));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
Now when I try to use this usercontrol in my mainpage.xaml and use the following binding: <test.TestControl ElementName="{Binding name}" />, it keeps searching for 'name' property in my custom usercontrol instead of where it should come from?
What am I doing wrong ?
It searches there because you have the DataContext set on the topmost level for your user control. What you would need to do is get rid of the relative binding to self in the user control and specify ElementName in bindings (inside user control). Btw you probably don't need OnPropertyChanged in the PropertyChangedCallback cause DependencyProperties in their nature notify about value changes.
I eventually solved it this way. Not the way I wanted, but it's a (in my eyes) pretty neat solution.
CustomUserControl.xaml
<UserControl x:Class="TestApp.Controls.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Width="75"
Height="75">
<Canvas x:Name="LayoutRoot"
Background="Black">
<StackPanel Orientation="Vertical">
<Image x:Name="UCImage"
Width="50"
Height="50"
HorizontalAlignment="Center" />
<TextBlock x:Name="UCText"
HorizontalAlignment="Center" />
</StackPanel>
</Canvas>
</UserControl>
CustomUserControl.xaml.cs
public partial class ElementControl : UserControl
{
#region DependencyProperty ElementNameProperty
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) =>
{
//See Here
((ElementControl)s).UCText.Text = e.NewValue as string;
})));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
#endregion
}

Categories

Resources