Application Settings binding to instance specific UserControl - wpf

I have three controls in my application of type MyUserControl. MyUserControl contains a Label which I want to bind to a string located in the Application Settings. Each of the three instances of MyUserControl has its own string in the Application Settings named Description1, Description2 and Description3 respectively.
The problem is that I cannot set the path of the binding in the UserControl to the name of the string located in the Application Settings because then every instance of MyUserControl would bind to the same string.
I have managed to get something working but as I have learned while working with WPF is that the solution I come up with is never the best way to do things :), so I was wondering if there is a better way to do this? Below is the relevant code I am using now:
MyUserControl.xaml.cs
public partial class MyUserControl : UserControl
{
public static DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(MyUserControl));
public string Description { get { return (string)GetValue(DescriptionProperty); } set { SetValue(DescriptionProperty, value); } }
}
MyUserControl.xaml
<UserControl x:Class="MyApplication.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"
xmlns:local="clr-namespace:MyApplication"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Name="myUserControl">
...
<Label Content="{Binding ElementName=myUserControl, Path=Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,6" />
MainWindow.xaml
xmlns:properties="clr-namespace:MyApplication.Properties"
...
<local:MyUserControl Description="{Binding Source={x:Static properties:Settings.Default}, Path=Description1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="myUserControl1" DataContext="{Binding ElementName=MainWindow, Path=Params}" />
<local:MyUserControl Description="{Binding Source={x:Static properties:Settings.Default}, Path=Description2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="myUserControl2" DataContext="{Binding ElementName=MainWindow, Path=Params}" />

Related

DataContext in WPF throws an exception

<Window x:Class="WpfApplication1.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>
<TextBox Name="myTxt" Text="{Binding}" />
</Grid>
</Window>
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = "fdfsfds";
}
}
}
I wonder why this code isn't working? It throws an exception. What should I do to bind textBox?
The default Binding for TextBox.Text property - is TwoWay
"Two-way binding requires Path or XPath."
So, you can use OneWay Binding:
<Grid>
<TextBox Name="myTxt" Text="{Binding Mode=OneWay}" />
</Grid>
If you still want TwoWay binding you may use this code:
<TextBox Name="myTxt" Text="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}" />

Does VS2010 show data in UserControls during design time?

I have a trivial user control:
<UserControl x:Class="Xxx.SimpleUserControl.SimpleTextUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<StackPanel Background="Blue">
<TextBlock x:Name="TitleTextBlock" Text="{Binding ElementName=root, Path=Title}" Background="White" Width="200" Height="30" Margin="5" />
<TextBlock Text="{Binding ElementName=root, Path=Time}" Background="White" Width="200" Height="30" Margin="9" />
</StackPanel>
</UserControl>
and the code behind:
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace Xxx.SimpleUserControl
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class SimpleTextUserControl : UserControl
{
public SimpleTextUserControl()
{
InitializeComponent();
}
[Browsable(true)]
[Category("SimpleControl")]
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(SimpleTextUserControl), new FrameworkPropertyMetadata("hello"));
[Browsable(true)]
[Category("SimpleControl")]
public DateTime Time
{
get { return (DateTime)GetValue(TimeProperty); }
set { SetValue(TimeProperty, value); }
}
// Using a DependencyProperty as the backing store for Time. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TimeProperty =
DependencyProperty.Register("Time", typeof(DateTime), typeof(SimpleTextUserControl), new UIPropertyMetadata(DateTime.Now));
}
}
I naively expect the VS2010 designer for the UserControl to display my default metadata values for my two controls - "hello" in one textblock, and today's date and time in other, but they are empty.
If I compile, and drop the control in to a WPF application, it renders fine, but not whilst in the UserControl project xaml view/designer.
I've tried changing the datacontext around, binding in different ways, implementing OnPropertyChanged etc., but nothing makes the data render in the UserControl project's design view.
Does anyone know the answer to this one? I've searched around, and either it's so obvious I'm missing it, or it's 'just the way it is'.
I think you'll need to use a "DesignTime" DataContext. Add the following to your UserControl Xaml file
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YourNamespace"
mc:Ignorable="d"
And then set the DesignTime DataContext with
d:DataContext="{d:DesignInstance local:SimpleTextUserControl,
IsDesignTimeCreatable=True}"
And remove ElementName from the Bindings
<UserControl x:Class="YourNamespace.SimpleTextUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YourNamespace"
mc:Ignorable="d"
x:Name="root"
d:DataContext="{d:DesignInstance local:SimpleTextUserControl,
IsDesignTimeCreatable=True}">
<StackPanel Background="Blue">
<TextBlock x:Name="TitleTextBlock" Text="{Binding Path=Title}" Background="White" Width="200" Height="30" Margin="5" />
<TextBlock Text="{Binding Path=Time}" Background="White" Width="200" Height="30" Margin="9" />
</StackPanel>
</UserControl>
If you're still having problems getting this to work I uploaded a small sample project which you can compare to: http://www.mediafire.com/?gan28oeel4qf7ik

DependencyProperty in UserControl doesn't get updated from binding

Here is the reproduction of my problem:
Create a WPF Application
Add a new UserControl to the project
Replace its content with the following
<UserControl
x:Class="UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding MyText}"/>
</UserControl>
Public Class UserControl1
Public Property MyText As String
Get
Return GetValue(MyTextProperty)
End Get
Set(ByVal value As String)
SetValue(MyTextProperty, value)
End Set
End Property
Public Shared ReadOnly MyTextProperty As DependencyProperty =
DependencyProperty.Register("MyText", GetType(String), GetType(UserControl1))
End Class
Replace the following in the MainWindow.xaml file:
<Window
x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication1"
Title="MainWindow"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel >
<TextBox Text="{Binding Title, UpdateSourceTrigger=PropertyChanged}"/>
<src:UserControl1 MyText="{Binding Title}"/>
</StackPanel>
</Window>
As you can see, the UserControl1.MyText property doesn't get updated when the MainWindow.Title changes.
What am I missing? I want the user control to be connected to the parent property, is there a xamly way to do it?
Okay, when I recreate the app (I'm using C#, so I don't think this will make any difference), I notice in my output the binding is failing because UserControl does not have a Title property.
Add ElementName=this to your UserControl1 binding. And set the Name property on the Window to this and that should fix it.
Something like this:
<src:UserControl1 MyText="{Binding Title, ElementName=this}" />
The binding works for me when I do that. Hope that helps!

WPF -- Anyone know why I can't get this binding to reference?

<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3"
DataContext="{Binding Mode=OneWay, Source={StaticResource local:oPatients}}">
I'm getting StaticResource reference 'local:oPatients' was not found.
Here is the codebehind:
public partial class MainWindow : Window
{
ListBox _activeListBox;
clsPatients oPatients;
public MainWindow()
{
oPatients = new clsPatients(true);
...
To be able to address the object as a StaticResource, it needs to be in a resource dictionary. However, since you're creating the object in MainWindow's constructor, you can set the DataContext in the code-behind like so.
oPatients = new clsPatients(true);
stkWaitingPatients.DataContext = oPatients;
And then change the Binding to this:
{Binding Mode=OneWay}
This is an ok practice if you're not going to be changing the DataContext again, otherwise you'd want a more flexible solution.
Edit: You mentioned ObjectDataProvider in your comment. Here's how you'd do that. First, add an xmlns:sys to the Window for the System namespace (I'm assuming you already have one for xmlns:local):
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Then you can add an ObjectDataProvider to your resource dictionary like this:
<Window.Resources>
<ObjectDataProvider
x:Key="bindingPatients"
ObjectType="{x:Type local:clsPatients}">
<ObjectDataProvider.ConstructorParameters>
<sys:Boolean>True</sys:Boolean>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</Window.Resources>
And refer to it in a Binding with the StaticResource markup like this, using the same string we specified in the x:Key attached property we gave it in the dictionary:
{Binding Source={StaticResouce bindingPatients}, Mode=OneWay}
Edit 2: Ok, you posted more code in your answer, and now I know why it's throwing an exception during the constructor. You're attempting to do this...
lstWaitingPatients.DataContext = oPatients;
... but lstWaitingPatients doesn't actually exist until after this.InitializeComponent() finishes. InitializeComponent() loads the XAML and does a bunch of other things. Unless you really need to do something before all of that, put custom startup code after the call to InitalizeComponent() or in an event handler for Window's Loaded event.
The following sets the ItemsSource in Code Behind and correctly handles the DataBinding:
public partial class MainWindow : Window
{
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//assuming oPatients implements IEnumerable
this.lstWaitingPatients.ItemsSource = oPatients;
And the XAML:
<ListBox x:Name="lstWaitingPatients"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291"
ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
Now, I can't get this to work...I get a general Windows startup error.
Here is the codebehind with the Initializer and the class being instantiated:
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
lstWaitingPatients.DataContext = oPatients;
this.InitializeComponent();
Here's the top of my XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Orista_Charting"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
x:Class="Orista_Charting.MainWindow"
x:Name="windowMain"
Title="Orista Chart"
Width="1024" Height="768" Topmost="True" WindowStartupLocation="CenterScreen" Activated="MainWindow_Activated" >
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/ButtonStyles.xaml"/>
<ResourceDictionary Source="Resources/OtherResources.xaml"/>
<ResourceDictionary Source="Resources/TextBlockStyles.xaml"/>
<ResourceDictionary Source="Resources/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
Here's the pertinent XAML, as you see, I went ahead and moved the DataContext down to the ListBox from the StackPanel. This doesn't run, but it does render in Design View (however, with no data present in the ListBox):
<!-- Waiting Patients List -->
<Border BorderThickness="1,1,1,1" BorderBrush="#FF000000" Padding="10,10,10,10"
CornerRadius="10,10,10,10" Background="#FFFFFFFF" Margin="15.245,187.043,0,41.957" HorizontalAlignment="Left" >
<StackPanel x:Name="stkWaitingPatients" Width="300" Margin="0,0,0,-3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Waiting Patients:" VerticalAlignment="Center" FontSize="21.333" Margin="0,0,0,20"/>
<TextBlock HorizontalAlignment="Right" Margin="0,0,38.245,0" Width="139" Height="16"
Text="Minutes Waiting" TextWrapping="Wrap" Foreground="#FF9C2525" FontWeight="Bold" VerticalAlignment="Bottom"
TextAlignment="Right"/>
<!-- Too be implemented, this is the wait animation -->
<!--<Image x:Name="PollGif" Visibility="{Binding Loading}"
HorizontalAlignment="Left" Margin="100,0,0,0" Width="42.5" Height="42.5"
Source="Images/loading-gif-animation.gif" Stretch="Fill"/>-->
</StackPanel>
<ListBox x:Name="lstWaitingPatients"
DataContext="{Binding Mode=OneWay}" ItemsSource="{Binding Mode=OneWay}"
IsSynchronizedWithCurrentItem="true"
ItemTemplate="{StaticResource WaitingPatientsItemTemplate}"
FontSize="21.333" Height="423.291" ScrollViewer.VerticalScrollBarVisibility="Visible"
GotFocus="lstWaitingPatients_GotFocus"
/>
</StackPanel>
</Border>
Ok, but if I just take comment out the assigment line in the codebehind, it does run (albeit with no data in the listbox):
public partial class MainWindow : Window
{
ListBox _activeListBox;
public MainWindow()
{
clsPatients oPatients = new clsPatients(true);
//lstWaitingPatients.DataContext = oPatients;
THANKS!

WPF binding user control with data in C# code

I've create user control like this:
public partial class View
{
public View()
{
InitializeComponent();
}
public static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(TeaserView) );
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
XAML:
<UserControl x:Class="Controls.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="164">
<Grid VerticalAlignment="Stretch"
x:Name="Preview">
<Label Height="28" Content="{Binding ElementName=Preview, Path=Name}" Background="LightYellow" x:Name="name" VerticalAlignment="Top" ></Label>
</Grid>
</UserControl>
and use it in Window1 simply in XAML:
<controls:View Height="200" Name="View1" Width="164" />
and I try set the Content in C# (Name property in this sample) but it does'n work, label's content is still empty. (All refereces, etc. are good) What's wrong?
Your code is wrong. You bind to Grid.Name property, which is "Preview", not to View.Name.
I really encourage you to go read from A to Z "DataBinding Overview" on MSDN. It worth your time, trust me :). In fact whole "Windows Presentation Foundation" section would be worth your attention.
As for your code, the following will work:
<UserControl x:Class="WpfApplication5.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300"
Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Label Height="28"
Content="{Binding Path=Name}"
Background="LightYellow"
VerticalAlignment="Top"/>
</Grid>
</UserControl>
But are you sure you want to hide "Name" property from parents?
Have you set the datacontext on the user control? Try setting it to point to its own codebehind:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
I've put the Name property just as sample. I'm trying to set Label Content in Window1.xaml.cs like:
View1.Name = "Casablanca";
Try the following binding, it should work:
<Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:View}}, Path=Name}" />
You should also define a xmlns:local="whatever_path_you_have" on the top of the file.
I also suggest renaming "Name" DP to something else to avoid name collusion.
Copied your exact code and it works fine.
However, it's not doing what you're probably expecting it to do. You're setting the source of the binding to the Grid instance. Therefore, the Name property will yield "Preview". The Name property you've defined in your UserControl is ignored because there's already a Name property on UserControl.

Resources