How to read a passed parameter in a WPF UserControl? - wpf

I created a user control in WPF:
<UserControl x:Class="TestUserControl.Controls.GetLatest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock Name="theTextBlock"/>
</UserControl>
The code behind has a parameter called "FirstMessage" which it sets as the text of my user control TextBlock:
public partial class GetLatest : UserControl
{
public string FirstMessage { get; set; }
public GetLatest()
{
InitializeComponent();
theTextBlock.Text = this.FirstMessage;
}
}
In my main code I can set the FirstMessage parameter in my user control with intellisense:
<Window x:Class="TestUserControl.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:controls="clr-namespace:TestUserControl.Controls"
>
<StackPanel>
<controls:GetLatest FirstMessage="This is the title"/>
</StackPanel>
</Window>
However, it still doesn't set the text. I've tried Text="{Binding Path=FirstMessage}" and other syntaxes I have found but nothing works.
How can I access the FirstMessage value in my user control?

Your approach to binding doesn't work because your property FirstMessage doesn't notify when it gets updated. Use Dependency Properties for that. See below:
public partial class GetLatest : UserControl
{
public static readonly DependencyProperty FirstMessageProperty = DependencyProperty.Register("FirstMessage", typeof(string), typeof(GetLatest));
public string FirstMessage
{
get { return (string)GetValue(FirstMessageProperty); }
set { SetValue(FirstMessageProperty, value); }
}
public GetLatest()
{
InitializeComponent();
this.DataContext = this;
}
}
XAML:
<UserControl x:Class="TestUserControl.Controls.GetLatest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock Text="{Binding FirstMessage}" />
</UserControl>
Whenever the FirstMessage property changes, your text block will update itself.

FirstMessage is set after the constructor has been called.
You should change your Text from the setter of FirstMessage.
When initializing object from XAML, first the default constructor is called, then the properties are set on the object.

This quick example won't use any binding because the value isn't set up until after the Default Constructor is called, but here's how you can get the text to show up.
<UserControl x:Class="TestUserControl.Controls.GetLatest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Loaded="GetLatest_Loaded">
<TextBlock Name="theTextBlock"/>
</UserControl>
Then, just modify your cs file to this:
public partial class GetLatest : UserControl
{
public string FirstMessage { get; set; }
public GetLatest()
{
InitializeComponent();
theTextBlock.Text = this.FirstMessage;
}
private void GetLatest_Loaded(object sender, RoutedEventArgs e)
{
theTextBlock.Text = this.FirstMessage;
}
}
I recommend working on setting up a Binding instead, as this is fairly spaghetti-like code.

You can also use:
public partial class GetLatest : UserControl
{
private string _firstMessage;
public string FirstMessage
{
get { return _firstMessage; }
set { _firstMessage = value; theTextBlock.Text = value; }
}
public GetLatest()
{
InitializeComponent();
}
}

In the case of the code you posted above it is a timing issue; the FirstMessage property has not had its value assigned when the constructor executes. You'd have to execute that code in an event occuring later on such as Loaded.

Related

How to use Message.Attach to call methods inside another object

I have a view model which has another class set as its property. The another class contains implementations of ICommand as its properties. I would like to execute one of the commands on a double click.
Unfortunatelly, Caliburn.Micro raises an exception instead ("No target found for method Commands.Command.Execute.").
I've tried to search the net and read the documentation, but without any success.
How to do it correctly?
Note: In real application, the message might be attached to a grid view which might have a different DataContext than the view model containing the commands class.
The XAML:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org">
<Grid>
<TextBox
cal:Message.Attach="[Event MouseDoubleClick]
= [Action Commands.Command.Execute(null)]" />
</Grid>
</Window>
The code-behind class:
namespace WpfApplication8
{
public partial class MainWindow
{
public Commands Commands { get; set; }
public MainWindow()
{
this.Commands =
new Commands { Command = new Command { MainWindow = this } };
this.InitializeComponent();
this.DataContext = this;
}
}
public class Commands
{
public Command Command { get; set; }
}
public class Command
{
public MainWindow MainWindow { get; set; }
public void Execute(object parameter)
{
this.MainWindow.Title = "Executed";
}
}
}
As #alik commented, complete solution is:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org">
<Grid>
<TextBox
cal:Message.Attach="[Event MouseDoubleClick] = [Action Execute(null)]"
cal:Action.TargetWithoutContext="{Binding Commands.Command}"/>
</Grid>
</Window>

WPF Simple Binding to INotifyPropertyChanged Object

I've created the simplest binding. A textbox bound to an object in the code behind.
Event though - the textbox remains empty.
The window's DataContext is set, and the binding path is present.
Can you say what's wrong?
XAML
<Window x:Class="Anecdotes.SimpleBinding"
x:Name="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SimpleBinding" Height="300" Width="300" DataContext="MainWindow">
<Grid>
<TextBox Text="{Binding Path=BookName, ElementName=TheBook}" />
</Grid>
</Window>
Code behind
public partial class SimpleBinding : Window
{
public Book TheBook;
public SimpleBinding()
{
TheBook = new Book() { BookName = "The Mythical Man Month" };
InitializeComponent();
}
}
The book object
public class Book : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
private string bookName;
public string BookName
{
get { return bookName; }
set
{
if (bookName != value)
{
bookName = value;
OnPropertyChanged("BookName");
}
}
}
}
First of all remove DataContext="MainWindow" as this sets DataContext of a Window to a string MainWindow, then you specify ElementName for your binding which defines binding source as another control with x:Name="TheBook" which does not exist in your Window. You can make your code work by removing ElementName=TheBook from your binding and either by assigning DataContext, which is default source if none is specified, of a Window to TheBook
public SimpleBinding()
{
...
this.DataContext = TheBook;
}
or by specifying RelativeSource of your binding to the Window which exposes TheBook:
<TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=TheBook.BookName}"/>
but since you cannot bind to fields you will need to convert TheBook into property:
public partial class SimpleBinding : Window
{
public Book TheBook { get; set; }
...
}

Binding of Label not working

my label is showing no content . what i am trying to do is i have a usercontrol TemplateForPlan and i am getting the selected item from that usecontrol and after that i am coming to next usercontrol and that selected template name must be there in label content.
sorry for poor description . i am a newbie and just started to work on WPF.
<UserControl x:Class="ChaosMonkeyUI.TemplateForPlan"
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="344" d:DesignWidth="424" Name="TemplateForPlanUC">
and this this is the label on another UC to show selected template
<Label Content="{Binding ElementName=TemplateForPlanUC, Path=selectedTemplate.TemplateName }" Grid.Row="1" Grid.Column="1" Height="28" HorizontalAlignment="Stretch"
Name="labelTemplateName" VerticalAlignment="Stretch" Margin="10,5,0,5" />
this is .cs file of TemplateForPlan and
public partial class TemplateForPlan : UserControl
{
IList<TemplateType> template;
public int noOfElementSelected;
TemplateHelper xmlParser ;
NewChaosSteps parentNewChaosStepPageForNextButton;
public TemplateType selectedTemplate = null;
public TemplateForPlan( NewChaosSteps parentNewChaosStepPageForNextButton)
{
InitializeComponent();
this.parentNewChaosStepPageForNextButton = parentNewChaosStepPageForNextButton;
parentNewChaosStepPageForNextButton.EnableOrDisableNextButton("disable");
xmlParser = new TemplateHelper();
template = xmlParser.GetTemplates();
listTemplate.ItemsSource = template;
}
private void listTemplate_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
selectedTemplate = template[listTemplate.SelectedIndex];
parentNewChaosStepPageForNextButton.EnableOrDisableNextButton("enable");
}
and TemplateType is defined in other project and its defination is:
public partial class TemplateType
{
private TemplateRuleType[] templateRuleField;
private string templateNameField;
private string templateDescriptionField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("TemplateRule")]
public TemplateRuleType[] TemplateRule {
get {
return this.templateRuleField;
}
set {
this.templateRuleField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string TemplateName {
get {
return this.templateNameField;
}
set {
this.templateNameField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string TemplateDescription {
get {
return this.templateDescriptionField;
}
set {
this.templateDescriptionField = value;
}
}
}
please also give some good link so that i can properly understand binding . i am very much confused in it.
You cannot bind to a field.
listTemplate is an items control, so it will have a SelectedItem property which you can bind to a property in your code behind.
public TemplateType SelectedTemplate { get; set; }
Then change your Label binding:
<Label Content="{Binding ElementName=TemplateForPlanUC, Path=SelectedTemplate.TemplateName }" />
(Notice the change in capitalisation of the name in the Path. If you post the XAML for your ItemsControl in TemplateForPlanUC then I will include an example that suits your case in my answer).
You also need to ensure you implement INotifyPropertyChanged on your control, and ensure that your SelectedTemplate property notifies in its setter. I won't detail that here because it has been covered a billion times before here on StackOverflow.

Basic Silverlight Binding

I can't figure out why the binding changes in my large project wont work. I have simplified it down to a sample project that still doesn't work. I would like to continue to set the datacontext the way I currently am if possible because that is how the other project does it. With the following code the text in SomeText is not showing up in the textbox. How do I fix this?
Code Behind:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
Data Class:
public class ViewModel
{
public string SomeText = "This is some text.";
}
Main User Control:
<UserControl xmlns:ig="http://schemas.infragistics.com/xaml" x:Class="XamGridVisibilityBindingTest.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:XamGridVisibilityBindingTest="clr-namespace:XamGridVisibilityBindingTest" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<TextBox Text="{Binding SomeText}" BorderBrush="#FFE80F0F" Width="100" Height="50"> </TextBox>
</Grid>
</UserControl>
Edit: I am only trying to do one-way binding.
You need to use a property, and make your VM inherit from INotifyPropertyChanged and raise the PropertyChanged event whenever SomeText changes:
public class ViewModel : INotifyPropertyChanged
{
private string someText;
public event PropertyChangedEventHandler PropertyChanged;
public string SomeText
{
get { return someText; }
set
{
someText = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SomeText"));
}
}
}
public ViewModel()
{
SomeText = "This is some text.";
}
}
I figured it out, you can only bind to Properties!
public class ViewModel
{
public string SomeText { get; set; }
public ViewModel()
{
SomeText = "This is some text.";
}
}

Func property on user control

I have a user control which I would like to add a dependency property of type Func so I can assign it a method handler in XAML. However, this will cause an XAMLParseException: 'Func`2' type does not have a public TypeConverter class. What am I doing wrong? Do I need to implement a TypeConverter for Func or is there a better way?
The Func Dependency Property in the user control (MyUserControl):
public Func<int, int> MyFunc
{
get { return (Func<int, int>)GetValue(MyFuncProperty); }
set { SetValue(MyFuncProperty, value); }
}
public static readonly DependencyProperty MyFuncProperty =
DependencyProperty.Register("MyFunc",
typeof(Func<int, int>),
typeof(SillyCtrl),
new UIPropertyMetadata(null));
Example of using the DP, XAML:
<Window x:Class="FuncTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:FuncTest="clr-namespace:FuncTest"
Title="Window1" Height="300" Width="300">
<Grid>
<FuncTest:MyUserControl MyFunc="SquareHandler" />
</Grid>
</Window>
Code behind:
namespace FuncTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
SquareHandler = (arg => arg * arg);
DataContext = this;
}
public Func<int, int> SquareHandler { get; set; }
}
}
MyFunc="SquareHandler"
Means set "MyFunc" property to a "SquareHandler" string and that's why it asks you for a TypeConverter able to converting strings into Funcs, change it to
<FuncTest:MyUserControl MyFunc="{Binding SquareHandler}" />
to use SquareHandler property of the current DataContext.

Resources