Binding a property with another property owned to a user control - c#

I want to realize a binding between several properties. Is it possible ?
I have a main window class named "MainWindow" which owns a property "InputText". This class contains a user control named MyUserControl. MyUserControl has a text box bound to a dependency property "MyTextProperty"
I would like to bind the property "InputText" of my main window with the dependency property "MyTextProperty" of my user control. So, if the user writes a text, I want that the properties "InputText", "MyTextProperty", "MyText" are updated.
User control code:
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MyUserControl.xaml
/// </summary>
public partial class MyUserControl : UserControl
{
public string MyText
{
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register("MyText", typeof(string), typeof(MyUserControl), new PropertyMetadata(0));
public MyUserControl()
{
this.DataContext = this;
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
}
WPF user control code:
<UserControl x:Class="WpfApplication1.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"
d:DesignHeight="25 " d:DesignWidth="100"
Background="Black">
<Grid>
<TextBox Height="20" Width="100" Text="{Binding MyText}"></TextBox>
</Grid>
</UserControl>
Main window code:
using System;
using System.Linq;
using System.Windows;
using System.ComponentModel;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string inputText;
public string InputText
{
get { return inputText; }
set
{
inputText = value;
NotifyPropertyChanged("InputText");
}
}
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
}
WPF main window code:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myNS="clr-namespace:WpfApplication1"
Title="MainWindow" Height="80" Width="300">
<Grid>
<StackPanel Orientation="Vertical">
<myNS:MyUserControl x:Name="test" MyText="{Binding InputText}"></myNS:MyUserControl>
<Button Name="cmdValidation" Content="Validation" Height="20"></Button>
</StackPanel>
</Grid>
</Window>
Thank you !

If you want your posted code to work with as little changes as possible then:
In MainWindow.xaml, change
MyText="{Binding InputText}"
to
MyText="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.InputText, Mode=TwoWay}"
You need TwoWay if you want the UC to update InputText.
Also, in MyUserControl.xaml.cs, in your DependencyProperty.Register statement, you have the PropertyMetadata default value set to 0 for a string -- change it to something appropriate for a string - like null or string.empty.
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register("MyText", typeof(string), typeof(MyUserControl), new PropertyMetadata(null));
If you want to change the code up a bit, you could make this more complex in the user control but simpler when you use it by:
Making the dependency property, MyText, bind two way by default
Stop setting the DataContext in the user control
Change the UC xaml text binding to use a relative source to the UC
I always find code easier to understand, so here are modified versions of your files:
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myNS="clr-namespace:WpfApplication1"
Title="MainWindow" Height="180" Width="300">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock>
<Run Text="MainWindow.InputText: " />
<Run Text="{Binding InputText}" />
</TextBlock>
<TextBlock>
<Run Text="MyUserControl.MyText: " />
<Run Text="{Binding ElementName=test, Path=MyText}" />
</TextBlock>
<myNS:MyUserControl x:Name="test" MyText="{Binding InputText}"></myNS:MyUserControl>
<Button Name="cmdValidation" Content="Validation" Height="20"></Button>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.ComponentModel;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string inputText = "Initial Value";
public string InputText
{
get { return inputText; }
set
{
inputText = value;
NotifyPropertyChanged("InputText");
}
}
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
}
MyUserControl.xaml
<UserControl x:Class="WpfApplication1.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"
d:DesignHeight="25 " d:DesignWidth="100"
Background="Black">
<Grid>
<TextBox Height="20" Width="100" Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=MyText, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</Grid>
</UserControl>
MyUserControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MyUserControl.xaml
/// </summary>
public partial class MyUserControl : UserControl
{
public string MyText
{
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register("MyText", typeof(string), typeof(MyUserControl), new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });
public MyUserControl()
{
InitializeComponent();
}
}
}

Firstly,
this.DataContext = this;
No. Just, no. You are overriding the DataContext of the UserControl set by it's parent window.
For your UserControl, give it an x:Name, and bind directly to the dependency property.
<UserControl
...
x:Name="usr">
<TextBox Text="{Binding MyText, ElementName=usr}" ... />
After you've done that, you can then simply bind your MyText property to the DataContext of the MainWindow.
<myNS:MyUserControl x:Name="test" MyText="{Binding InputText}" />

Related

WinUI 3 - How to open a control in a page upon click of button from a User Control c#?

I have an ItemsRepeater on the page's XAML code where it's ItemsSource property is bind to a list of User Control (ObersvableCollection), a custom control I made. In this User Control there's a button that I wish would open a SplitView pane that I set in the Page's Xaml code. I'm thinking I need to get an instance of the page in the User Control's code behind, on the click event, but I have no idea how.
You can do it this way.
TestUserControl.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Windows.Input;
namespace WinUI3App1;
public sealed partial class TestUserControl : UserControl
{
public static readonly DependencyProperty ClickCommandProperty = DependencyProperty.Register(
nameof(ClickCommand),
typeof(ICommand),
typeof(TestUserControl),
new PropertyMetadata(null));
public TestUserControl()
{
InitializeComponent();
}
public ICommand ClickCommand
{
get => (ICommand)GetValue(ClickCommandProperty);
set => SetValue(ClickCommandProperty, value);
}
}
TestUseControl.xaml
<UserControl
x:Class="WinUI3App1.TestUserControl"
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"
x:Name="ThisControl">
<StackPanel Orientation="Horizontal">
<Button Command="{x:Bind ClickCommand}" CommandParameter="{Binding ElementName=ThisControl}" Content="Click" />
</StackPanel>
</UserControl>
MainWindow.xaml.cs
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace WinUI3App1;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Install the CommunityToolkit.Mvvm NuGet package
// to avoid implementing commands yourself.
ClickCommand = new RelayCommand<TestUserControl>(OnClick);
for (int i = 0; i < 10; i++)
{
TestUserControls.Add(new TestUserControl()
{
ClickCommand = ClickCommand
});
}
}
public ObservableCollection<TestUserControl> TestUserControls { get; set; } = new();
public ICommand ClickCommand { get; set; }
private void OnClick(TestUserControl? sender)
{
SplitViewControl.IsPaneOpen = true;
}
}
MainWindow.xaml
<Window
x:Class="WinUI3App1.MainWindow"
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">
<SplitView x:Name="SplitViewControl">
<SplitView.Pane>
<Grid/>
</SplitView.Pane>
<StackPanel Orientation="Vertical">
<ItemsRepeater ItemsSource="{x:Bind TestUserControls}" />
</StackPanel>
</SplitView>
</Window>

WPF, doesn't work OnPropertyChanged for Properties.Settings (StringCollection) in listView

I try to set content for listView from Properties.Settings (StringCollection). Contet set successful, but if i delete item, listView don't refresh. If i close and open SettingWindow, content inside listView is correct. It's mean, something wrong in DataBinding, probably doesn't work OnPropertyChanged.
SettingWindow.xaml:
<Window x:Class="FilmDbApp.Views.SettingWindow"
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"
xmlns:p="clr-namespace:FilmDbApp.Properties"
xmlns:local="clr-namespace:FilmDbApp.Views"
mc:Ignorable="d"
Title="Setting" Height="500" Width="400" ResizeMode="NoResize" WindowStartupLocation="CenterOwner">
<DockPanel>
<TabControl>
<TabItem Header="Genre options">
<StackPanel>
<ListView ItemsSource="{Binding Source={x:Static p:Settings.Default}, Path=Genres, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedGenre}"" VerticalAlignment="Top"/>
<Button Command="{Binding DeleteGenreCommand}" CommandParameter="{Binding SelectedGenre}" Content="Delete"/>
</StackPanel>
</TabItem>
<TabItem Header="Autosave options"/>
</TabControl>
</DockPanel>
</Window>
SettingWindow.cs:
using System.Windows;
using FilmDbApp.ViewModels;
namespace FilmDbApp.Views
{
/// <summary>
/// Interaction logic for SettingWindow.xaml
/// </summary>
public partial class SettingWindow : Window
{
public SettingWindow()
{
InitializeComponent();
DataContext = new SettingWindowViewModel();
}
}
}
SettingWindowViewModel.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using FilmDbApp.Views;
using FilmDbApp.Models;
using FilmDbApp.Utils;
namespace FilmDbApp.ViewModels
{
class SettingWindowViewModel : INotifyPropertyChanged
{
private string selectedGenre;
public string SelectedGenre
{
get { return selectedGenre; }
set
{
selectedGenre = value;
OnPropertyChanged("SelectedGenre");
}
}
public SettingWindowViewModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
// Delete genre
private RelayCommand deleteGenreCommand;
public RelayCommand DeleteGenreCommand
{
get
{
return deleteGenreCommand ??
(deleteGenreCommand = new RelayCommand(obj =>
{
string genre = obj as string;
Properties.Settings.Default.Genres.Remove(genre);
Properties.Settings.Default.Save();
OnPropertyChanged("Genres");
}, (obj) => Properties.Settings.Default.Genres.Count > 0 && obj != null));
}
}
}
}
Instead of binding to some other property in some other source you can utilize the power of ViewModel, which is used to work in between of view and models.
Add following property to a ViewModel
class SettingWindowViewModel : INotifyPropertyChanged
{
public List<Genre> Genres => Properties.Settings.Default.Genres;
...
}
and bind to it
<ListView ItemsSource="{Binding Genres}"
SelectedItem="{Binding SelectedGenre}"... />
Now inside various commands you should be able to tell bindings to update
OnPropertyChanged(nameof(Genres));

Usercontrol binding lost on value updated, that doesn't happen with standard textbox control

I have a problem with binding in usercontrol.
This is my usercontrol:
UserControl1.xaml
<UserControl x:Class="WpfApp1.UserControl1"
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-ompatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
x:Name="usercontrol"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBox Text="{Binding HmiField, ElementName=usercontrol}"/>
</Grid>
</UserControl>
UserControl1.xaml.cs
namespace WpfApp1
{
public partial class UserControl1 : UserControl
{
public double HmiField
{
get { return (double)GetValue(HmiFieldProperty); }
set { SetValue(HmiFieldProperty, value); }
}
public static readonly DependencyProperty HmiFieldProperty =
DependencyProperty.Register("HmiField", typeof(double), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
}
}
And this is the main window:
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
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"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
DataContext="{Binding Md, RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="450" Width="800">
<UniformGrid>
<Button Content="{Binding Prop1}" Click="Button_Click"/>
<Label Content="{Binding Prop1}"/>
<TextBox Text="{Binding Prop1}"/>
<local:UserControl1 HmiField="{Binding Prop1}"/>
</UniformGrid>
</Window>
MainWindow.xaml.cs
namespace WpfApp1
{
public class tMd: INotifyPropertyChanged
{
#region Interfaccia INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
#endregion
private double prop1;
public double Prop1 { get
{
return prop1;
}
set
{
if (prop1 != value)
{
prop1 = value;
NotifyPropertyChanged("Prop1");
}
}
}
}
public partial class MainWindow : Window
{
public tMd Md
{
get { return (tMd)GetValue(MdProperty); }
set { SetValue(MdProperty, value); }
}
public static readonly DependencyProperty MdProperty =
DependencyProperty.Register("Md", typeof(tMd), typeof(MainWindow), new PropertyMetadata(new tMd()));
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Md.Prop1 = 1234.5678;
}
}
}
I found some similar question:
How do I change TextBox.Text without losing the binding in WPF?
WPF: Binding is lost when bindings are updated
WPF Textbox TwoWay binding in datatemplate not updating the source even on LostFocus
But I can't completely understand what's happening: why a standard textbox work as expected and my usercontrol no?
Or better: is there a way to have my usercontrol works with the textbox's behaviour?
The Binding must be TwoWay, either set explicitly
<local:UserControl1 HmiField="{Binding Prop1, Mode=TwoWay}"/>
or implicitly by default:
public static readonly DependencyProperty HmiFieldProperty =
DependencyProperty.Register(
nameof(HmiField), typeof(double), typeof(UserControl1),
new FrameworkPropertyMetadata(
0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
A TextBox's Text property is registered like shown above, i.e. with the BindsTwoWayByDefault flag.
At the TextBox Binding in the UserControl's XAML you may also want to update the source property while the user is typing (instead of only on lost focus):
<TextBox Text="{Binding HmiField,
ElementName=usercontrol,
UpdateSourceTrigger=PropertyChanged}"/>
or without the otherwise useless generated usercontrol field:
<TextBox Text="{Binding HmiField,
RelativeSource={RelativeSource AncestorType=UserControl}
UpdateSourceTrigger=PropertyChanged}"/>
Your Prop1 notifies when it changes, but you haven't told you binding to trigger on that notification.
Try including UpdateSourceTrigger=PropertyChanged in you binding

Binding visibility any Parent

Hi I this two different modes to binding visibility from a parent's property .
for standar control is possible tom use this:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Button x:Name="buttonTest" Width="75" Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"/>
but to my usercontrol I only this way to binding the visibility , otherwise doesn't works:
<local:UserControl4 Height="100" Width="100" Visibility="{Binding ElementName=Window1,Path=IsModify,Converter={StaticResource BoolToVis}}"/>
My question is why I need to pass the element name plus ParentProperty at my control visibility , but for button no?
this is the codeBehind
public static readonly DependencyProperty IsModifyProperty =
DependencyProperty.Register("IsModify", typeof(Boolean), typeof(MainWindow), new PropertyMetadata(false));
[DefaultValue(false)]
public Boolean IsModify
{
get { return (Boolean)GetValue(MainWindow.IsModifyProperty); }
set
{
SetValue(MainWindow.IsModifyProperty, value);
this.OnPropertyChanged("IsModify");
}
}
and this is the constructor
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
here the source code to replicate the error.
you needs one project with one window and one usercontrol.
source code for window
---- XAML ----
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:XTesting" x:Name="windowTest" x:Class="XTesting.WindowTest"
Title="WindowTest" Height="300" Width="583.908">
<Window.Resources>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<local:UserControl4
HorizontalAlignment="Left"
Height="65"
Margin="300,30,0,0"
VerticalAlignment="Top"
Width="230"
Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"
Background="#FFF86161" Description="Custom Control"/>
<Button x:Name="buttonTest"
Content="Modify"
HorizontalAlignment="Left"
Margin="45,30,0,0"
VerticalAlignment="Top"
Width="225"
Visibility="{Binding IsModify, Converter={StaticResource BoolToVis}}"
Height="65"/>
<Button x:Name="button"
Content="Skisem (Click me)"
HorizontalAlignment="Left"
Height="75"
Margin="225,160,0,0"
VerticalAlignment="Top"
Width="135"
Click="button_Click_1"
Cursor="Hand"/>
</Grid>
this is the codebehind of window
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace XTesting
{
/// <summary>
/// Interaction logic for WindowTest.xaml
/// </summary>
public partial class WindowTest : Window
{
public static readonly DependencyProperty IsModifyProperty =
DependencyProperty.Register("IsModify", typeof(Boolean), typeof(WindowTest), new PropertyMetadata(false));
[DefaultValue(false)]
public Boolean IsModify
{
get { return (Boolean)GetValue(WindowTest.IsModifyProperty); }
set
{
SetValue(WindowTest.IsModifyProperty, value);
this.OnPropertyChanged("IsModify");
}
}
public WindowTest()
{
InitializeComponent();
this.DataContext = this;
}
private void button_Click_1(object sender, RoutedEventArgs e)
{
IsModify = !IsModify;
}
#region - INotifyPropertyChanged implementation -
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion - INotifyPropertyChanged implementation -
}
}
this is the source code for usercontrol
XAML:
<UserControl x:Class="XTesting.UserControl4"
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="37" d:DesignWidth="310" MinWidth="7"
MouseLeftButtonUp="Selector1_MouseLeftButtonUp" FontSize="20">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="GridRoot">
<Grid.RowDefinitions>
<RowDefinition Height="37*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="51"/>
<ColumnDefinition Width="259*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextBoxDescription"
Margin="0"
TextWrapping="Wrap"
Grid.Column="1"
Text="{Binding Description}"
Background="{x:Null}"
BorderBrush="{x:Null}"
Foreground="{Binding Foreground,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
FontSize="{Binding FontSize,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
VerticalContentAlignment="Center"
IsReadOnly="True"
SelectionBrush="{x:Null}"
BorderThickness="0"
Focusable="False"
IsTabStop="False"
IsUndoEnabled="False"
AllowDrop="False"
Padding="1,0,0,0"
MaxLines="1"
/>
</Grid>
and this is the code behind:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace XTesting
{
/// <summary>
/// Interaction logic for UserControl4.xaml
/// </summary>
public partial class UserControl4 : UserControl, INotifyPropertyChanged
{
#region - Delegate -
public delegate void ClickHandler(Object sender, RoutedEventArgs e);
#endregion - Delegate -
#region - Events -
public event ClickHandler Click;
#endregion - Events -
#region - Dependency Properties mandatory for Binding -
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(String), typeof(UserControl4), new PropertyMetadata(String.Empty));
#endregion - Dependency Properties for Binding -
#region - Properties -
/// <summary>
/// Gets or sets the description
/// </summary>
[Category("Common"), Description("gets or sets The description")]
public String Description
{
get { return (String)GetValue(UserControl4.DescriptionProperty); }
set
{
SetValue(UserControl4.DescriptionProperty, value);
this.OnPropertyChanged("Description");
}
}
#endregion - Properties -
public UserControl4()
{
InitializeComponent();
this.DataContext = this;
}
private void Selector1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (Click != null) Click(sender, e);
}
#region - INotifyPropertyChanged implementation -
// Basically, the UI thread subscribes to this event and update the binding if the received Property Name correspond to the Binding Path element
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion - INotifyPropertyChanged implementation -
}
}
I think the problem is in the constructor This.datacontext= this;
but this is mandatory if i want binding the property description.
Thanks in advance
You should probably add the user control DataContext on the GridRoot element instead of the UserControl4 class itself. The user control usage should be controlled mainly by the instantiating code and only its content should be controlled by the user control itself.
Also, I prefer to assign the data context on the Loaded event instead of the constructor, but this might just be my personal preference.
<UserControl x:Class="XTesting.UserControl4" Loaded="UserControl4_Loaded" ...
// ...
private void UserControl4_Loaded(object sender, RoutedEventArgs e)
{
GridRoot.DataContext = this;
}

Dynamically set TextBlock's text binding

I am attempting to write a multilingual application in Silverlight 4.0 and I at the point where I can start replacing my static text with dynamic text from a SampleData xaml file. Here is what I have:
My Database
<SampleData:something xmlns:SampleData="clr-namespace:Expression.Blend.SampleData.MyDatabase" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<SampleData:something.mysystemCollection>
<SampleData:mysystem ID="1" English="Menu" German="Menü" French="Menu" Spanish="Menú" Swedish="Meny" Italian="Menu" Dutch="Menu" />
</SampleData:something.mysystemCollection>
</SampleData:something>
My UserControl
<UserControl
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"
x:Class="Something.MyUC" d:DesignWidth="1000" d:DesignHeight="600">
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MyDatabase}}">
<Grid Height="50" Margin="8,20,8,0" VerticalAlignment="Top" d:DataContext="{Binding mysystemCollection[1]}" x:Name="gTitle">
<TextBlock x:Name="Title" Text="{Binding English}" TextWrapping="Wrap" Foreground="#FF00A33D" TextAlignment="Center" FontSize="22"/>
</Grid>
</Grid>
</UserControl>
As you can see, I have 7 languages that I want to deal with. Right now this loads the English version of my text just fine. I have spent the better part of today trying to figure out how to change the binding in my code to swap this out when I needed (lets say when I change the language via drop down).
It sounds like you're looking for code like this:
Title.SetBinding(TextProperty, new Binding { Path = new PropertyPath(language) });
All it does is create a new Binding for the language you requested and use it to replace the old binding for the Title's Text property.
You are going about this the wrong way. Best practice for localization in Silverlight is to use resource files holding the translated keywords. Here is some more info about this:
http://msdn.microsoft.com/en-us/library/cc838238%28VS.95%29.aspx
EDIT:
Here is an example where I use a helper class to hold the translated strings. These translations could then be loaded from just about anywhere. Static resource files, xml, database or whatever. I made this in a hurry, so it is not very stable. And it only switches between english and swedish.
XAML:
<UserControl x:Class="SilverlightApplication13.MainPage"
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"
xmlns:local="clr-namespace:SilverlightApplication13"
mc:Ignorable="d"
d:DesignWidth="640"
d:DesignHeight="480">
<UserControl.Resources>
<local:TranslationHelper x:Key="TranslationHelper"></local:TranslationHelper>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel>
<TextBlock Margin="10"
Text="{Binding Home, Source={StaticResource TranslationHelper}}"></TextBlock>
<TextBlock Margin="10"
Text="{Binding Contact, Source={StaticResource TranslationHelper}}"></TextBlock>
<TextBlock Margin="10"
Text="{Binding Links, Source={StaticResource TranslationHelper}}"></TextBlock>
<Button Content="English"
HorizontalAlignment="Left"
Click="BtnEnglish_Click"
Margin="10"></Button>
<Button Content="Swedish"
HorizontalAlignment="Left"
Click="BtnSwedish_Click"
Margin="10"></Button>
</StackPanel>
</Grid>
</UserControl>
Code-behind + TranslationHelper class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.ComponentModel;
namespace SilverlightApplication13
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
//Default
(this.Resources["TranslationHelper"] as TranslationHelper).SetLanguage("en-US");
}
private void BtnEnglish_Click(object sender, RoutedEventArgs e)
{
(this.Resources["TranslationHelper"] as TranslationHelper).SetLanguage("en-US");
}
private void BtnSwedish_Click(object sender, RoutedEventArgs e)
{
(this.Resources["TranslationHelper"] as TranslationHelper).SetLanguage("sv-SE");
}
}
public class TranslationHelper : INotifyPropertyChanged
{
private string _Contact;
/// <summary>
/// Contact Property
/// </summary>
public string Contact
{
get { return _Contact; }
set
{
_Contact = value;
OnPropertyChanged("Contact");
}
}
private string _Links;
/// <summary>
/// Links Property
/// </summary>
public string Links
{
get { return _Links; }
set
{
_Links = value;
OnPropertyChanged("Links");
}
}
private string _Home;
/// <summary>
/// Home Property
/// </summary>
public string Home
{
get { return _Home; }
set
{
_Home = value;
OnPropertyChanged("Home");
}
}
public TranslationHelper()
{
//Default
SetLanguage("en-US");
}
public void SetLanguage(string cultureName)
{
//Hard coded values, need to be loaded from db or elsewhere
switch (cultureName)
{
case "sv-SE":
Contact = "Kontakt";
Links = "Länkar";
Home = "Hem";
break;
case "en-US":
Contact = "Contact";
Links = "Links";
Home = "Home";
break;
default:
break;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}

Categories