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));
}
}
}
}
Related
I am making a To-do List application for one of my classes. Now I have tried in a console app to update objects (of TaskModels a type I defined) inside an observable collection and it has seemed to work. But now I am trying to update the entries inside the observable collection. I have wrote some code that I thought was going to change the value but it did not change the value in the ListBox I will include the code below for TaskModel.
Originally I was wanting to have the edit button open a new window that allowed the user to input what the wanted the task to be and then they press a button maybe called change task and it sends it back to the original window. But for simpleness at the moment I would like to change with the textbox in the user control in the main window.
I am also pretty new to making apps in WPF so this is all pretty new to me and I am trying to learn it all.
Below is my TaskModel and the only thing it does is get and set the TaskName.
namespace ToDoList.Model
{
public class TaskModel
{
private string taskName;
public string TaskName {
get { return taskName; }
set { taskName = value; }
}
}
}
I wrote the following code hoping that it would allow me to change the value of the TaskName but it does not seem to be working. Is there anyhting else that I should add to the code for it change the TaskName properly? Any tips or anything that could help me with this problem.
Below is the XAML code for my main window. It is a really simple UI, it features a ListBox which uses an ObservableCollection as its itemsource and when the listbox has a new item put into it has a checkbox to the left of it.
Below I will include the main window XAML and the C# code.
<Window x:Class="ToDoList.DemoMainWindow"
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:ToDoList"
mc:Ignorable="d"
Title="The To-Do List" Height="500" Width="500" FontSize="22"
Background="White">
<Grid Margin="10" Background="BlueViolet">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2">
<TextBlock>Tasks to do:</TextBlock>
<!-- <TextBlock>Blah blah</TextBlock> -->
<!-- <local:UCLabelTextBxInput x:Name="TxtUCSaveToFileLocation" Title="Save to File Location" MaxLength="50"></local:UCLabelTextBxInput> -->
<ListBox x:Name="LstBoxTasks" MinHeight="200" MaxHeight="200" SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Mode=OneWay}" Content="{Binding TaskName, Mode=TwoWay}" FontSize="14"></CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<local:UCLabelTextBxInput x:Name="TxtUCEnteredTask" Title="Enter Task Here:" MaxLength="50"></local:UCLabelTextBxInput>
<Button x:Name="BtnAddTask" Click="BtnAddTask_Click" Background="Chocolate">Add Task to List</Button>
</StackPanel>
<Button Grid.Column="0" Grid.Row="1" Margin="0,10,20,0" x:Name="btnDeleteTask" Click="BtnDeleteTask_Click" Background="Chocolate">Delete Task</Button>
<Button Grid.Column="1" Grid.Row="1" Margin="20,10,0,0" Background="Chocolate" Click="BtnEditTask_Click">Edit Task</Button>
<Button Grid.Column="0" Grid.Row="2" Margin="0,10,0,10" Grid.ColumnSpan="2" Background="Chocolate" Click="BtnHelp_Click">Help</Button>
</Grid>
</Window>
Then finally here is the C# code:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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;
using ToDoList.Model;
namespace ToDoList
{
/// <summary>
/// Interaction logic for DemoMainWindow.xaml
/// </summary>
public partial class DemoMainWindow : Window
{
SaveDataModel saveDataModel = new SaveDataModel();
ObservableCollection<TaskModel> tasksModels = new ObservableCollection<TaskModel>();
public DemoMainWindow()
{
InitializeComponent();
TxtUCEnteredTask.txtLimitedInput.Text = "Do the dishes";
LstBoxTasks.ItemsSource = tasksModels;
}
private void BtnAddTask_Click(object sender, RoutedEventArgs e)
{
tasksModels.Add(new TaskModel() { TaskName = TxtUCEnteredTask.txtLimitedInput.Text });
}
private void BtnDeleteTask_Click(object sender, RoutedEventArgs e)
{
if(LstBoxTasks.SelectedItem != null)
{
tasksModels.Remove(LstBoxTasks.SelectedItem as TaskModel);
}
}
private void BtnHelp_Click(object sender, RoutedEventArgs e)
{
HelpWindow helpWindow = new HelpWindow();
helpWindow.Show();
}
private void BtnEditTask_Click(object sender, RoutedEventArgs e)
{
if (LstBoxTasks.SelectedItem != null)
{
tasksModels[LstBoxTasks.SelectedIndex].TaskName = TxtUCEnteredTask.txtLimitedInput.Text;
}
}
}
}
Your property TaskName is not calling the OnPropertyChanged() method that notifies your view that the name changed.
You can do something like that:
private string taskName;
public string TaskName {
get { return taskName; }
set {
if( value != taskName) {
taskName = value;
OnPropertyChanged("TaskName");
}
}
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
Remarks:
The best way to do WPF is to do MVVM.
You should check out this link : https://www.codeproject.com/Tips/806587/Basic-MVVM-Listbox-Binding-in-WPF
I have a small WPF app that I am working on that uses the Xceed BusyIndicator. I'm having some trouble dynamically updating the loading message because the content is contained within a DataTemplate. The methods I'm familiar with for data binding, or setting the value of text aren't working.
I've done some research - and it looks like others have had this issue. It seems it was answered here, but because the answer was not in context I cannot quite figure out how that would work in my code.
Here is my sample code, if someone could help me understand what I'm missing I would greatly appreciate it. This has the added challenge of using a BackgroundWorker thread. I use this because I anticipate this will be a long running progress - ultimately the action will start a SQL Job that will process items that may take up to 15 minutes. My plan is to have the thread periodically run a stored procedure to get a count of remaining items to process and update the loading message.
MainWindow.xaml:
<Window x:Class="WPFTest.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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:WPFTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<xctk:BusyIndicator x:Name="AutomationIndicator">
<xctk:BusyIndicator.BusyContentTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock Text="Sending Invoices" FontWeight="Bold" HorizontalAlignment="Center"/>
<WrapPanel>
<TextBlock Text="Items remaining: "/>
<TextBlock x:Name="_ItemsRemaining" Text="{Binding Path=DataContext.ItemsRemaining, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
</WrapPanel>
</StackPanel>
</DataTemplate>
</xctk:BusyIndicator.BusyContentTemplate>
<Grid>
<StackPanel>
<TextBlock Text="Let's test this thing" />
<Button x:Name="_testBtn" Content="Start" Width="100" HorizontalAlignment="Left" Click="testBtn_Click"/>
<TextBlock Text="{Binding ItemsRemaining}"/>
</StackPanel>
</Grid>
</xctk:BusyIndicator>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
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 WPFTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
UpdateItemsRemaining(0);
}
public class ItemCountDown
{
//One Idea was to try and set a data binding variable
public string ItemsRemaining { get; set; }
}
public void UpdateItemsRemaining(int n)
{
ItemCountDown s = new ItemCountDown();
{
s.ItemsRemaining = n.ToString();
};
//this.AutomationIndicator.DataContext = s; Works during initiation, but not in the thread worker.
}
private void testBtn_Click(object sender, RoutedEventArgs e)
{
//Someone clicked the button, run the Test Status
TestStatus();
}
public void TestStatus()
{
// Normally I'd start a background worker to run a loop to check that status in SQL
BackgroundWorker getStatus = new BackgroundWorker();
getStatus.DoWork += (o, ea) =>
{
//Normally there's a sql connection being opened to check a SQL Job, and then I run a loop that opens the connection to check
//the status until it either fails or successfully ended.
//but for this test, I'll just have it run for 15 seconds, counting down fake items.
int fakeItems = 8;
do //
{
//Idea One - write to the text parameter. But can't find it in the template
//Dispatcher.Invoke((Action)(() => _ItemsRemaining.Text = fakeItems));
//Idea two - use data binding to update the value. Data binding works just find outside of the Data Template but is ignored in the template
UpdateItemsRemaining(fakeItems);
//subtract one from fake items and wait a second.
fakeItems--;
Thread.Sleep(1000);
} while (fakeItems > 0);
};
getStatus.RunWorkerCompleted += (o, ea) =>
{
//work done, end it.
AutomationIndicator.IsBusy = false;
};
AutomationIndicator.IsBusy = true;
getStatus.RunWorkerAsync();
}
}
}
Thank you for reviewing and I appreciate any help or direction given.
Set the DataContext to your ItemCountDown object and implement INotifyPropertyChanged:
public class ItemCountDown : INotifyPropertyChanged
{
private string _itemsRemaining;
public string ItemsRemaining
{
get { return _itemsRemaining; }
set { _itemsRemaining = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public partial class MainWindow : Window
{
private readonly ItemCountDown s = new ItemCountDown();
public MainWindow()
{
InitializeComponent();
DataContext = s;
UpdateItemsRemaining(0);
}
public void UpdateItemsRemaining(int n)
{
s.ItemsRemaining = n.ToString();
}
private void testBtn_Click(object sender, RoutedEventArgs e)
{
TestStatus();
}
public void TestStatus()
{
...
}
}
You can then bind directly to the property in the DataTemplate of the XAML markup:
<TextBlock x:Name="_ItemsRemaining" Text="{Binding Path=DataContext.ItemsRemaining, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
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;
}
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}" />
This is my scenario: My DataTemplate of a ListView contains a TextBox and some buttons, one of the buttons is used to select and highlight all of the text in the TextBox. I can find many solutions for select and highlight text in TextBox from code behind, but none of them define the TextBox and the Button in DataTemplate. Anyone can help?
Thanks
You can do something like this below :
XAML :
<Window x:Class="SOWPF.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">
<Grid>
<ListView Width="200" Height="300" ItemsSource="{Binding FriendList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Margin="2" Text="{Binding Name}"></TextBox>
<Button Content="Select" Click="Button_Click"></Button>
<Button Content="Delete"></Button>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Code Behind :
using System.Windows;
using System.Windows.Controls;
namespace SOWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var friendViewModel = new FriendViewModel();
friendViewModel.AddFriends();
this.DataContext = friendViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var parent = (StackPanel)((sender as Button).Parent);
var children = parent.Children;
foreach(var child in children)
{
if (child.GetType().Equals(typeof(TextBox)))
{
var tb = child as TextBox;
tb.Focus();
tb.SelectAll();
break;
}
}
}
}
}
ViewModel :
using System.Collections.ObjectModel;
namespace SOWPF
{
public class FriendViewModel
{
public ObservableCollection<Friend> FriendList
{ get; set; }
public void AddFriends()
{
FriendList = new ObservableCollection<Friend>();
FriendList.Add(new Friend() { Name = "Arpan" });
FriendList.Add(new Friend() { Name = "Nrup" });
FriendList.Add(new Friend() { Name = "Deba" });
}
}
public class Friend
{
public string Name { get; set; }
}
}
Probably would be a good using an Attached property set on the button, and in the attached code using a code something like written by cvraman.
Using this way you absolutely avoid the code behind structure, and better way to using mvvm