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.
Related
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; }
...
}
I am trying to work out wpf with some difficulties. This ComboBox seems a very basic issue but I can't have it populated even after reading all possible similar post.
The extra difficulty I think is that the ComboBox is defined in a resource, here is the resource code:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:DiagramDesigner">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/Shared.xaml"/>
<ResourceDictionary Source="Styles/ToolBar.xaml"/>
</ResourceDictionary.MergedDictionaries>
<ToolBar x:Key="MyToolbar" Height="120">
<!--Languages-->
<GroupBox Header="Localization" Style="{StaticResource ToolbarGroup}" Margin="3">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Center"
VerticalAlignment="Top" Width="120"
ItemsSource="{Binding _langListString}"
DisplayMemberPath="ValueString"
SelectedValuePath="ValueString"
SelectedValue="{Binding LangString}"
/>
</Grid>
</GroupBox>
</ToolBar>
My data object is defined as follow:
public partial class Window1 : Window
{
List<ComboBoxItemString> _langListString = new List<ComboBoxItemString>();
// Object to bind the combobox selections to.
private ViewModelString _viewModelString = new ViewModelString();
public Window1()
{
// Localization settings
_langListString.Add(new ComboBoxItemString()); _langListString[0].ValueString = "en-GB";
_langListString.Add(new ComboBoxItemString()); _langListString[1].ValueString = "fr-FR";
_langListString.Add(new ComboBoxItemString()); _langListString[2].ValueString = "en-US";
// Set the data context for this window.
DataContext = _viewModelString;
InitializeComponent();
}
And the modelview:
/// This class provides us with an object to fill a ComboBox with
/// that can be bound to string fields in the binding object.
public class ComboBoxItemString
{
public string ValueString { get; set; }
}
//______________________________________________________________________
//______________________________________________________________________
//______________________________________________________________________
/// Class used to bind the combobox selections to. Must implement
/// INotifyPropertyChanged in order to get the data binding to
/// work correctly.
public class ViewModelString : INotifyPropertyChanged
{
/// Need a void constructor in order to use as an object element
/// in the XAML.
public ViewModelString()
{
}
private string _langString = "en-GB";
/// String property used in binding examples.
public string LangString
{
get { return _langString; }
set
{
if (_langString != value)
{
_langString = value;
NotifyPropertyChanged("LangString");
}
}
}
#region INotifyPropertyChanged Members
/// Need to implement this interface in order to get data binding
/// to work properly.
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
I just don't know what to try else. Is anyone has an idea of what is going on, and why the combobox stays empty?
Many thanks.
you can just bind to public properties
ItemsSource="{Binding _langListString}"
can not work because _langListString is not a public property
By my analysis the problem consist in your DataContext.
DataContext = _viewModelString;
If you give the viewModelString to the DataContext you have to have the _langListString >defined there, in order to the combobox know which item it is bound to.
This is what I would do:
Add List _langListString = new List(); to the >ModelView.
_langListString would be _viewModelString._langListString.add(Your Items) - be >carefull to instatiate the _langList when you create your _viewModelString object.
Then I think the rest would work.
Many thanks, I have the changes you've suggested but this combobox still stays empty :-(
The new modelview looks like this:
/// Class used to bind the combobox selections to. Must implement
/// INotifyPropertyChanged in order to get the data binding to
/// work correctly.
public class ViewModelString : INotifyPropertyChanged
{
public List<ComboBoxItemString> _langListString {get;set;}
/// Need a void constructor in order to use as an object element
/// in the XAML.
public ViewModelString()
{
// Localization settings
_langListString = new List<ComboBoxItemString>();
ComboBoxItemString c;
c = new ComboBoxItemString(); c.ValueString = "en-GB"; _langListString.Add(c);
c = new ComboBoxItemString(); c.ValueString = "fr-FR"; _langListString.Add(c);
c = new ComboBoxItemString(); c.ValueString = "en-US"; _langListString.Add(c);
}
private string _langString = "en-GB";
/// String property used in binding examples.
public string LangString
{
get { return _langString; }
set
{
if (_langString != value)
{
_langString = value;
NotifyPropertyChanged("LangString");
}
}
}
#region INotifyPropertyChanged Members
/// Need to implement this interface in order to get data binding
/// to work properly.
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
The data object:
// Object to bind the combobox selections to.
private ViewModelString _viewModelString;
public Window1()
{
// Set the data context for this window.
_viewModelString = new ViewModelString();
DataContext = _viewModelString;
InitializeComponent();
}
And I have tried all possible combination in the combobox (_langListString, _viewModelString._langListString, _viewModelString) it just doesn't work:
<ComboBox Height="23" HorizontalAlignment="Center"
VerticalAlignment="Top" Width="120"
ItemsSource="{Binding _langListString}"
DisplayMemberPath="ValueString"
SelectedValuePath="ValueString"
SelectedValue="{Binding LangString}"
/>
I tend to think that this xaml is making things really complicated without possibility of debugging. Is anyone can help???
I am using the WPF AutoCompleteBox and I have it working great, but one thing I would like to do is sort the suggestion list on the fly after each letter is entered into the primary TextBox. Does anyone know how to do this? I tried using an ICollectionView property with the DefaultView logic and adding SortDescriptions but it doesn't seem to phase the suggestion list. To make sure my collection view sorting was working I put a normal ListBox control and an AutoCompleteBox control on the same window and bound both controls to the same observable collection with the same collection view and the normal ListBox control showed the items sorted correctly using the SortDescriptions, but the AutoCompleteBox list didn't have the items sorted. It had them in the order they were added to the collection.
Thoughts? Suggestions? Has anyone done this?
I have no idea how #user1089031 done this, but here is working sample for anyone who could be interested in (updated to #adabyron's comment!):
ViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
namespace WpfApplication12
{
public class Item
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
public class ViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate {};
private readonly ObservableCollection<Item> source;
private readonly ICollectionView items;
private string searchText;
public ViewModel()
{
source = new ObservableCollection<Item>
{
new Item {Name = "111111111 Test abb - (1)"},
new Item {Name = "22222 Test - (2)"},
new Item {Name = "333 Test - (3)"},
new Item {Name = "44444 Test abc - (4)"},
new Item {Name = "555555 Test cde - (5)"},
new Item {Name = "66 Test - bbcd (6)"},
new Item {Name = "7 Test - cd (7)"},
new Item {Name = "Test - ab (8)"},
};
items = new ListCollectionView(source);
}
public ICollectionView Items
{
get { return items; }
}
public IEnumerable<Item> ItemsSorted
{
get
{
return string.IsNullOrEmpty(SearchText)
? source
: (IEnumerable<Item>)source
.OrderBy(item => item.Name.IndexOf(SearchText,
StringComparison.InvariantCultureIgnoreCase));
}
}
public Item Selected { get; set; }
public string SearchText
{
get { return searchText; }
set
{
searchText = value;
PropertyChanged(this,
new PropertyChangedEventArgs("SearchText"));
PropertyChanged(this,
new PropertyChangedEventArgs("ItemsSorted"));
}
}
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
xmlns:wpfApplication2="clr-namespace:WpfApplication12"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="200" Width="500"
DataContext="{DynamicResource viewModel}">
<Window.Resources>
<wpfApplication2:ViewModel x:Key="viewModel" />
<DataTemplate DataType="{x:Type wpfApplication2:Item}">
<TextBlock Text="{Binding Name}" FontFamily="Courier New" />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<controls:AutoCompleteBox
ItemsSource="{Binding ItemsSorted}"
FilterMode="ContainsOrdinal"
SelectedItem="{Binding Selected, Mode=TwoWay}"
MinimumPrefixLength="0"
VerticalAlignment="Top" Margin="5">
<i:Interaction.Behaviors>
<wpfApplication2:SearchTextBindBehavior
BoundSearchText="{Binding SearchText,
Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
</controls:AutoCompleteBox>
<ListBox Grid.Column="1"
ItemsSource="{Binding Items}" Margin="5" />
</Grid>
</Window>
As you could notice I've add one custom behavior to AutoCompleteBox control:
<i:Interaction.Behaviors>
<wpfApplication2:SearchTextBindBehavior
BoundSearchText="{Binding SearchText,
Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
This is because AutoCompleteBox's own SearchText property is read-only. So here is the code of this behavior:
SearchTextBindBehavior.cs (Updated)
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace WpfApplication12
{
public class SearchTextBindBehavior : Behavior<AutoCompleteBox>
{
public static readonly DependencyProperty BoundSearchTextProperty =
DependencyProperty.Register("BoundSearchText",
typeof(string), typeof(SearchTextBindBehavior));
public string BoundSearchText
{
get { return (string)GetValue(BoundSearchTextProperty); }
set { SetValue(BoundSearchTextProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += OnTextChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= OnTextChanged;
}
private void OnTextChanged(object sender, RoutedEventArgs args)
{
if(AssociatedObject.Text.Length == 0)
{
BoundSearchText = string.Empty;
return;
}
if(AssociatedObject.SearchText ==
AssociatedObject.Text.Substring(0,
AssociatedObject.Text.Length - 1))
{
BoundSearchText = AssociatedObject.Text;
}
}
}
}
Note: To make it all work you will need to add reference to the System.Windows.Interactivity.dll from the Expression Blend 4 SDK. This is just where Behavior<T> and a few its friends live.
If you have Expression Blend already installed, you already have all the SDK and there is no need to download anything. Just in case - on my machine the assembly located here:
C:\Program Files\Microsoft SDKs\Expression\Blend.NETFramework\v4.0\Libraries\System.Windows.Interactivity.dll
And, finally, if you have some good reason to do NOT add reference to this popular official library, feel free to re-implemented this custom behavior in "the old way" via plain old attached properties.
Hope that helps.
This is what I ended up with, a slight adaptation of Sevenate's answer, so if you wanted to upvote, do that to his post.
I used a subclass (I had the AutoCompleteBox subclassed already for other reasons), which allows me to create a wrapper dependency property to get the readonly SearchText (=what the user entered via keyboard) to the ViewModel - instead of a blend behavior, which is a perfectly valid way, too.
The crux of the matter is that you should only apply the dynamic sorting upon changes of SearchText, not Text (=what is displayed in the AutoCompleteBox, will also change if a suggestion is selected in the dropdown). Sevenate's way to raise the PropertyChanged event of the readonly ItemsSource (ItemsSorted) is a nice and clean way to apply the sorting.
ViewModel:
public class Item
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
public class AutoCompleteBoxDynamicSortingVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly ObservableCollection<Item> source;
public AutoCompleteBoxDynamicSortingVM()
{
source = new ObservableCollection<Item>
{
new Item {Name = "111111111 Test abb - (1)"},
new Item {Name = "22222 Test - (2)"},
new Item {Name = "333 Test - (3)"},
new Item {Name = "44444 Test abc - (4)"},
new Item {Name = "555555 Test cde - (5)"},
new Item {Name = "66 Test - bbcd (6)"},
new Item {Name = "7 Test - cd (7)"},
new Item {Name = "Test - ab (8)"},
};
}
public IEnumerable<Item> ItemsSorted
{
get
{
return string.IsNullOrEmpty(Text) ? (IEnumerable<Item>)source :
source.OrderBy(item => item.Name.IndexOf(Text, StringComparison.OrdinalIgnoreCase));
}
}
public Item Selected { get; set; }
// Text that is shown in AutoCompleteBox
private string text;
public string Text
{
get { return text; }
set { text = value; OnPropertyChanged("Text"); }
}
// Text that was entered by user (cannot be changed from viewmodel)
private string searchText;
public string SearchText
{
get { return searchText; }
set
{
searchText = value;
OnPropertyChanged("SearchText");
OnPropertyChanged("ItemsSorted");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Subclass of AutoCompleteBox:
public class MyAutoCompleteBox : AutoCompleteBox
{
/// <summary>
/// Bindable property that encapsulates the readonly property SearchText.
/// When the viewmodel tries to set SearchText by way of EnteredText, it will fail without an exception.
/// </summary>
public string EnteredText
{
get { return (string)GetValue(EnteredTextProperty); }
set { SetValue(EnteredTextProperty, value); }
}
public static readonly DependencyProperty EnteredTextProperty = DependencyProperty.Register("EnteredText", typeof(string), typeof(MyAutoCompleteBox), new PropertyMetadata(null));
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
// synchronize SearchText and EnteredText (only one-way)
if (e.Property == AutoCompleteBox.SearchTextProperty && this.EnteredText != this.SearchText)
EnteredText = SearchText;
base.OnPropertyChanged(e);
}
}
Xaml:
<UserControl x:Class="WpfApplication1.Controls.AutoCompleteBoxDynamicSorting"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myctrls="clr-namespace:WpfApplication1.Controls"
xmlns:models="clr-namespace:WpfApplication1.ViewModels"
Height="350" Width="525"
DataContext="{DynamicResource viewModel}">
<UserControl.Resources>
<models:AutoCompleteBoxDynamicSortingVM x:Key="viewModel" />
<DataTemplate DataType="{x:Type models:Item}">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</UserControl.Resources>
<Grid>
<myctrls:MyAutoCompleteBox
ItemsSource="{Binding ItemsSorted}"
Text="{Binding Text, Mode=TwoWay}"
EnteredText="{Binding SearchText, Mode=OneWayToSource}"
FilterMode="ContainsOrdinal"
VerticalAlignment="Top" Margin="5" />
</Grid>
</UserControl>
I have an ObservableCollection of "Layouts" and a "SelectedLocation" DependencyProperty on a Window. The SelectedLocation has a property called "Layout", which is an object containing fields like "Name" etc. I'm trying to bind a combobox to the SelectedLayout but it's not working.
The following does not work, I've tried binding to SelectedItem instead to no avail. I believe it may be something to do with the fact that I'm binding to a subProperty of the SelectedLocation DependencyProperty (though this does implement INotifyPropertyChanged.
<ComboBox Grid.Row="2" Grid.Column="0" x:Name="cboLayout" ItemsSource="{Binding Layouts,ElementName=root}" SelectedValue="{Binding SelectedLocation.Layout.LayoutID,ElementName=root}" DisplayMemberPath="{Binding Name}" SelectedValuePath="LayoutID" />
However, the following works (Also bound to the "SelectedLocation" DP:
<TextBox Grid.Row="4" Grid.Column="1" x:Name="txtName" Text="{Binding SelectedLocation.Name,ElementName=root,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
What type property Layouts has? I suppose something like this this: IEnumerable<Layout>.
But you bind selected value to Layout.LayoutID. So you got situation, when combo box contains Layout objects, and you try to select it by Int identifier. Of course binding engine can't find any Int there.
I have no idea about details of your code, so one thing I could propose: try to reduce your binding expression: SelectedItem="{Binding SelectedLocation.Layout,ElementName=root}.
If no success, provide more code to help me understand what's going on.
====UPDATE====
As I've said, you are obviously doing something wrong. But I am not paranormalist and couldn't guess the reason of your fail (without your code). If you don't want to share your code, I decided to provide simple example in order to demonstrate that everything works. Have a look at code shown below and tell me what is different in your application.
Class Layout which exposes property LayoutId:
public class Layout
{
public Layout(string id)
{
this.LayoutId = id;
}
public string LayoutId
{
get;
private set;
}
public override string ToString()
{
return string.Format("layout #{0}", this.LayoutId);
}
}
Class SelectionLocation which has nested property Layout:
public class SelectedLocation : INotifyPropertyChanged
{
private Layout _layout;
public Layout Layout
{
get
{
return this._layout;
}
set
{
this._layout = value;
this.OnPropertyChanged("Layout");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var safeEvent = this.PropertyChanged;
if (safeEvent != null)
{
safeEvent(this, new PropertyChangedEventArgs(name));
}
}
}
And Window class with dependency properties (actually, in my example StartupView is UserControl, but it doesn't matter):
public partial class StartupView : UserControl
{
public StartupView()
{
InitializeComponent();
this.Layouts = new Layout[] { new Layout("AAA"), new Layout("BBB"), new Layout("CCC") };
this.SelectedLocation = new SelectedLocation();
this.SelectedLocation.Layout = this.Layouts.ElementAt(1);
}
public IEnumerable<Layout> Layouts
{
get
{
return (IEnumerable<Layout>)this.GetValue(StartupView.LayoutsProperty);
}
set
{
this.SetValue(StartupView.LayoutsProperty, value);
}
}
public static readonly DependencyProperty LayoutsProperty =
DependencyProperty.Register("Layouts",
typeof(IEnumerable<Layout>),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
public SelectedLocation SelectedLocation
{
get
{
return (SelectedLocation)this.GetValue(StartupView.SelectedLocationProperty);
}
set
{
this.SetValue(StartupView.SelectedLocationProperty, value);
}
}
public static readonly DependencyProperty SelectedLocationProperty =
DependencyProperty.Register("SelectedLocation",
typeof(SelectedLocation),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
}
XAML of StartupView:
<UserControl x:Class="Test.StartupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:HandyCopy"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Root">
<WrapPanel>
<ComboBox ItemsSource="{Binding Path=Layouts,ElementName=Root}"
SelectedItem="{Binding Path=SelectedLocation.Layout, ElementName=Root}"/>
</WrapPanel>
</UserControl>
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.