I have a simple User Control
Xaml:
<UserControl x:Class="GraemeGorman_Controls.Navigation.NavigationItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border x:Name="borderLayoutRoot">
<TextBlock x:Name="textBlockCaption" Text="{Binding Caption}" />
</Border>
</UserControl>
Cs:
namespace GraemeGorman_Controls.Navigation
{
public partial class NavigationItem : UserControl
{
public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register(
"Caption",
typeof (string),
typeof (NavigationItem),
new PropertyMetadata(new PropertyChangedCallback(OnCaptionChanged)));
public string Caption
{
get {return (string)GetValue(CaptionProperty);}
set {SetValue(CaptionProperty, value);}
}
public NavigationItem()
{
InitializeComponent();
}
private static void OnCaptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//null
}
}
}
What my issue is, when I create an instance of the control the caption never shows - now i have tested the property in the OnCaptionChanged function using e.NewValue and it is the correct value. What is wrong with my binding?
If I write in the code behind caption property for set
textBlockCaption.Text = value;
It appears fine...
Any help appreciated
Thanks
Graeme
From the code-behind it looks like you are missing one line of code.
Try to add DataContext = this; in your constructor. This has worked for me in the past.
How are you creating the instance of the NavigationItem control?
you'll need to do something like:
<Page ...
xmlns:gg="clr-namespace:GraemeGorman_Controls.Navigation">
<gg:NavigationItem Caption="FooBar" />
or even
<gg:NavigationItem Caption="{Binding Path=TheCaption}" />
where TheCaption is a property of your Page's DataContext (eg your ViewModel)
Hope This Helps :)
Related
I've made a user control which contains a command, to be called in response to a certain event. This command is a dependency property. I want to use it in the main window like this:
<local:myUserControl Command="{Binding someCommand}"/>
The "myCommand" is the dependency property I created for this user control. And I bind it to a command of the view model of the main window ("someCommand").
The problem is that I am setting the datacontext of my usercontrol (I have a view model for it), and it seems to reset the "Command" to null… Here is the code-behind of my view model:
public partial class myUserControl : UserControl, ICommandSource
{
public myUserControl()
{
this.DataContext = new myViewModel();
InitializeComponent();
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(myUserControl), new PropertyMetadata(null));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(myUserControl), new PropertyMetadata(0));
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(myUserControl), new PropertyMetadata(null));
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Command.Execute(this.CommandParameter);
}
}
The code of my user control could be the Following:
<UserControl x:Class="myApp.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:myApp"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock MouseUp="TextBlock_MouseUp">
</TextBlock>
</Grid>
</UserControl>
(I know that this element seems a bit silly (or useless), but I have simplified it to test what didn't worked and also in order to ask a rather simple question).
I have discovered that, if I comment the "this.DataContext = new myViewModel();" line, the binding to the command works perfectly. And when I uncomment this line and put a breakpoint in the "TextBlock_MouseUp", the "Command" property is equal to null...
Would there be a way to resolve this problem? I have some complicated code in my view model (so I'm quite forced to keep this line "this.DataContext = new myViewModel();"), and I am not sure I could find another solution than having a "Command" dependency property in my user control…
To be sure I give a maximum of informations, I have the following code in the view model of my main window:
public ICommand someCommand { get; set; }
//Constructor
public MainWindowViewModel()
{
this.someCommand = new RelayCommand((obj) => { return true; },
(obj) =>
{
//I put a breakpoint here
int dummy = 0;
});
}
(The RelayCommand class is a standard RelayCommand class, with a "Predicate" CanExecute and an "Action Execute).
I hope this question is not a duplicate… I have found several similar question, but they did not seem to answer mine...
I'm really sorry for this question which was in fact a bit silly. I hadn't understand very well what happens during a binding. I thought that this code line in the MainWindow…
<local:myUserControl Command="{Binding someCommand}"/>
…would have made an attempt to bind the UserControl's "Command" property to the "someCommand" of the datacontext of the MainWindow. In fact, as #elgonzo pointed out, the binding looks up in the UserControl's datacontext for the "someCommand" property (and not in the MainWindow's datacontext!!). Therefore, setting the UserControl's datacontext with this line…
this.DataContext = new myViewModel();
...was preventing the binding to be correctly done (since it looks for the "someCommand" property of the UserControl's datacontext, which is now "myViewModel", which does not contain "someCommand"...).
To fix this, I had to change the binding like this:
<local:myUserControl Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.someCommand}"/>
I've found this solution here: https://stackoverflow.com/a/1127964/11609068.
Maybe it is not the best way to do it (the "Path= DataContext. someCommand" make me think this, it doesn't seem very elegant), but it works. Another way to do it is to name the MainWindow (x:Name="someName"), so that the binding is a bit simpler:
<local:myUserControl Command="{Binding ElementName=someName, Path=DataContext.someCommand}"/>
Again, sorry and many thanks to #elgonzo.
My goal is to create add Text property to RichTextBox. I created Attached Property and set binding to ViewModel's property. Unfortunately changing text in RichTextBox doesn't update underlying property.
Here is my code:
View.cs:
public partial class KnuthMorrisPrattView : UserControl
{
public KnuthMorrisPrattView()
{
InitializeComponent();
}
public static string GetText(DependencyObject obj)
{
return (string)obj.GetValue(TextProperty);
}
public static void SetText(DependencyObject obj, string value)
{
obj.SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached
(
"Text",
typeof(string),
typeof(KnuthMorrisPrattView),
new FrameworkPropertyMetadata()
{
BindsTwoWayByDefault = true,
PropertyChangedCallback = PropertyChangedCallback,
CoerceValueCallback = CoerceValueCallback,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.LostFocus
}
);
private static object CoerceValueCallback(DependencyObject dependencyObject, object baseValue)
{
throw new NotImplementedException();
}
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
throw new NotImplementedException();
}
}
View.xaml:
<UserControl x:Class="Launcher.Views.KnuthMorrisPrattView"
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:views="clr-namespace:Launcher.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500"
DataContext="{Binding KnuthMorrisPrattViewModel, Source={StaticResource MainViewModel}}">
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="7*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<Label Content="Text" DockPanel.Dock="Top"></Label>
<RichTextBox x:Name="TextBox" views:KnuthMorrisPrattView.Text="{Binding TextToSearchArg}"/>
</DockPanel>
<DockPanel Grid.Row="1">
<Label Content="Pattern" DockPanel.Dock="Top"></Label>
<TextBox Text="{Binding PatternArg}"/>
</DockPanel>
</Grid>
ViewModel.cs:
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using GalaSoft.MvvmLight.CommandWpf;
using Launcher.Runners.KnuthMorrisPratt;
namespace Launcher.ViewModels
{
public class KnuthMorrisPrattViewModel : ViewModelBase
{
private string _textToSearchArg;
private string _patternArg;
public string TextToSearchArg
{
get { return _textToSearchArg; }
set
{
_textToSearchArg = value;
RaisePropertyChanged();
}
}
public string PatternArg
{
get { return _patternArg; }
set
{
_patternArg = value;
RaisePropertyChanged();
}
}
public KnuthMorrisPrattViewModel()
{
}
}
}
I know that Callback throws and exception but my goal here is to just see under the debugger that this callback is invoked. Then I add correct implementation.
EDIT:
I think I missed important note about my issue. When I update TextToSearchArg property from code everything works correctly. The only problem is that when I set some text in RichTextBox underlying property is not updated.
What am I missing? Thanks a lot in advance.
Nothing in your code shows that the Attached property is bound to the RichTextBox events, hence it won't ever be called if the content/text in RichTextBox changes.
You'd need to subscribe to the RichTextBox.TextChanged event.
public partial class KnuthMorrisPrattView : UserControl
{
public KnuthMorrisPrattView()
{
InitializeComponent();
this.TextBox.TextChanged += OnTextChanged;
}
...
public void OnTextChanged(object sender, TextChangedEventArgs e)
{
// Get the text from the event and set your Text Property
string text = ...;
SetText(this, text);
}
}
Edit:
In case you want to listen to another control's Dependency/Attached Property changes, use
DependencyPropertyDescriptor.FromProperty(ControlClassName.DesiredPropertyProperty, typeof(ControlClassName)).AddValueChanged(dependencyObject, OnDesiredPropertyChanged);
Where...
ControlClassName is the class containing the Dependency Property (i.e. RichTextBox in your case or the class where the Dependency/Attached Property is defined
'DesiredPropertyPropertyis the name of your property (i.e.TextProperty`
dependencyObject is the instance of object where the DesiredPropertyProperty is set on
OnDesiredPropertyChanged method to call when the property value changes
On a side note:
You should have Code-Behind in the view. There is no requirement that Dependency Properties or Attached Properties have to be defined inside the same class as the are meant for.
Code behind should only be used, if it's an reusable User Control but the naming of your class suggest it's not a User Control (even though it derives form User Control) but a View.
A view is application specific and can't be reused outside that specific app and is only meant to display a certain content. If you make a "LoginControl" then it it can be made to be reusable in other. A "LoginView" on other side doesn't suggest re-usability.
Maybe
Mode=TwoWay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged
on the binding missing?
I've got an interesting UI problem. I've got a RadBusyIndicator from Telerik wrapped inside of a UserControl (for ease of switching to the Windows busy indicator if the Telerik one still has a memory leak). When I put content into the control, if it has anything more than a ContentControl in between the opening and closing tags of the wrapper control, everything with an x:Name attribute is null in the code behind and causes an exception when the page is loaded.
Here is a likeness of the code with names removed to protect the innocent.
The xaml...
<UserControl>
<Grid x:Name="Indicator">
<telerik:RadBusyIndicator x:Name="BusyIndicator" IsBusy="{Binding Path=IsStatusBusy, Mode=TwoWay}" BusyContent="{Binding Path=WaitingContent, Mode=TwoWay}" Content="{Binding Path=UserContent, Mode=TwoWay}"/>
</Grid>
</UserControl>
And the code behind...
[ContentProperty("UserContent")]
public partial class CustomBusyIndicator : UserControl
{
public CustomBusyIndicator()
{
InitializeComponent();
Indicator.DataContext = this;
}
public UIElement UserContent
{
get { return (UIElement)GetValue(UserContentProperty); }
set { SetValue(UserContentProperty, value); }
}
private static readonly DependencyProperty UserContentProperty = DependencyProperty.Register("PageContent",
typeof(UIElement), typeof(CustomBusyIndicator), new PropertyMetadata(null));
private static readonly DependencyProperty WaitingContentProperty = DependencyProperty.Register("WaitingContent",
typeof (object), typeof (CustomBusyIndicator), new PropertyMetadata(null, OnWaitingContentChanged));
private static void OnWaitingContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{}
private static readonly DependencyProperty IsStatusBusyProperty = DependencyProperty.Register("IsStatusBusy",
typeof (bool), typeof (CustomBusyIndicator), new PropertyMetadata(false, OnIsStatusBusyChanged));
private static void OnIsStatusBusyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{}
public bool IsStatusBusy
{
get { return (bool) GetValue(IsStatusBusyProperty); }
set { SetValue(IsStatusBusyProperty, value); }
}
public object WaitingContent
{
get { return GetValue(WaitingContentProperty); }
set { SetValue(WaitingContentProperty, value); }
}
}
And I'm using it like this.....
<CustomBusyIndicator IsStatus={Binding IsBusy}>
<CustomBusyIndicator.WaitingContent>
<TextBlock Text="Loading..." Foreground="Black" />
</CustomBusyIndicator.WaitingContent>
<Grid>
.
.
.
.
</Grid>
</CustomBusyIndicator>
Any ideas? Thanks in advance for your help!
Edit
I've now established that it is the x:Name that seems to be causing the issues. They are null in the code behind after InitializeComponent() is called.
You are deriving from UserControl, so what happens... let's see:
Your class inherits a public property called Content and exactly this property is the dedicated ContentProperty of the baseclass, caused by an annotation like [ContentProperty("Content")] at the baseclass level.
That's the reason why normaly everything you declare in the xaml-part of you userControl definition is showing up when loaded.
So when you see this...
<UserControl ... >
<Grid x:Name="Indicator">
<telerik:RadBusyIndicator x:Name="BusyIndicator" ... />
</Grid>
</UserControl>
it is technically the same as writing this:
<UserControl ... >
<UserControl.Content>
<Grid x:Name="Indicator">
<telerik:RadBusyIndicator x:Name="BusyIndicator" ... />
</Grid>
</UserControl.Content>
</UserControl>
That means whenever you use your UserControl somewhere in your xaml and add content the way you did...
<CustomBusyIndicator ...>
<Grid> ... </Grid>
</CustomBusyIndicator>
...you are overwriting everything that was declared inside the xaml-part of the UserControl definition (and it does not matter that you annotated another property to be the ContentProperty, this just means you set the new ContentProperty twice).
So what are your options now:
Option Number 1: Keep UserControl as your base, but use your property UserContent only explicitly
so the usage would look like this:
<CustomBusyIndicator ...>
<CustomBusyIndicator.UserContent>
<Grid> ... </Grid>
</CustomBusyIndicator.UserContent>
</CustomBusyIndicator>
Option Number 2: derive from Control or ContentControl and transform your UserControl's xaml-part into a default ControlTemplate
that way you can use it like this
<CustomBusyIndicator ...>
<Grid> ... </Grid>
</CustomBusyIndicator>
but you have to make sure your template is found when the xaml is parsed. I usually do the following:
create a ResourceDictionary CustomBusyIndicator.xaml
add an entry to themes/generic.xaml that includes the dictionary
add DefaultStyleKey = typeof(CustomBusyIndicator); to your control's constructor
add an implicit style to CustomBusyIndicator.xaml
And this has another ramification. You cannot use named elements as easily as before: you have to write an override for OnApplyTemplate and get references to those named elements via GetTemplateChild("BusyIndicator") as RadBusyIndicator;
Option Number 3: Keep UserControl as your base, and UserContent as the ContentProperty, but set the xaml-part explicitly
so the definition would look like this:
<UserControl ... >
<UserControl.Content>
<Grid x:Name="Indicator">
<telerik:RadBusyIndicator x:Name="BusyIndicator" ... />
</Grid>
</UserControl.Content>
</UserControl>
I have a user control which contains a textbox and have created a get/set in the usercontrol to get/set the text property of the textbox.
public class OpenFileControl : UserControl
{
StackPanel sp;
public TextBox tb;
public string Text { get { return tb.Text; } set { tb.Text = value; } }
I then want to set this value based on a binding later on -
<gX3UserControls:OpenFileControl Text="{Binding Value}" />
But I get the following exception
A 'Binding' cannot be set on the 'Text' property of type 'OpenFileControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
After some investigation It seems Text needs to be a dependency property, but If I do that I cant work out how to pass the value on to the textbox.
How can I fix this.
Consider using something like this.
Control XAML:
<UserControl x:Class="WpfTestBench.OpenFileControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=Filename, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</UserControl>
Control codebehind:
using System.Windows;
namespace WpfTestBench
{
public partial class OpenFileControl
{
public static readonly DependencyProperty FilenameProperty =
DependencyProperty.Register("Filename", typeof (string), typeof (OpenFileControl));
public OpenFileControl()
{
InitializeComponent();
}
public string Filename
{
get { return (string)GetValue(FilenameProperty); }
set { SetValue(FilenameProperty, value); }
}
}
}
Main XAML:
<Window x:Class="WpfTestBench.OpenFileWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfTestBench="clr-namespace:WpfTestBench"
Title="OpenFileWindow" Width="300" SizeToContent="Height">
<StackPanel>
<wpfTestBench:OpenFileControl x:Name="In" Filename="{Binding SelectedFilename, UpdateSourceTrigger=PropertyChanged}" />
<wpfTestBench:OpenFileControl x:Name="Out" Filename="{Binding ElementName=In, Path=Filename}" />
</StackPanel>
</Window>
Main codebehind:
namespace WpfTestBench
{
public partial class OpenFileWindow
{
public OpenFileWindow()
{
InitializeComponent();
DataContext = this;
}
public string SelectedFilename { get; set; }
}
}
Execution result (after typing something in the first control):
If you define the dependency property as the static and the actual property, you can write whatever code behind you want in the body of the property.
public const string TextPropertyName = "Text";
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
TextPropertyName,
typeof(string),
typeof(MyControl),
new UIPropertyMetadata(false));
In the getter and setter you can do something like textBox1.Text = value; but you'd probably be better served using a binding to the property instead. MVVM frameworks make light work of this sort of thing quite often. You might find more success defining a ViewModel (a class with an appropriate FielPath variable for example) and setting the DataContext of the new UserControl to be an instance of the ViewModel class, using Bindings to do the heavy lifting for you.
I am creating a custom "PageHeaderControl" UserControl, with a header property:
public partial class PageHeaderControl: UserControl
{
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header",
typeof(string), typeof(PageHeaderControl),
new PropertyMetadata(""));
public string Header
{
get { return GetValue(HeaderProperty) as string; }
set { SetValue(HeaderProperty, value); }
}
}
In the XAML for that control, I have:
<sdk:Label Content="{Binding Header,Mode=TwoWay}" />
Now for the problem: When I create the control, binding it only works to do this:
<my:PageHeaderControl Header="This is my page header" />
And it does not work to do this, where PageHeader is the property in my ViewModel holding the header value:
<my:PageHeaderControl Header="{Binding PageHeader,Mode=TwoWay}" />
I thought maybe my properties were messed up, but this also works:
<TextBlock Text="{Binding PageHeader,Mode=TwoWay}" />
Any ideas as to what the problem could be!
Thanks so much!!!
Edit:
In my ViewModel, PageHeader is this:
private string _pageHeader = "This is my page header";
public string PageHeader
{
get
{
return _pageHeader;
}
set
{
_pageHeader = value;
RaisePropertyChanged("PageHeader");
}
}
Edit 2:
When I put a breakpoint inside the "get" for my PageHeader property, it does not get hit AT ALL, unless I add in the TextBlock...
If I understand you correctly you're trying to bind a property of an element within your control's XAML markup to the property of the control itself.
If this is the case, see if the following helps you.
PageHeaderControl.xaml:
<UserControl x:Class="TryElementBinding.PageHeaderControl"
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"
x:Name = "MyControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding Header, ElementName=MyControl}"></TextBlock>
</Grid>
PageHeaderControl.xaml.cs:
public partial class PageHeaderControl : UserControl
{
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(PageHeaderControl), new PropertyMetadata(""));
public string Header
{
get
{
return GetValue(HeaderProperty) as string;
}
set
{
SetValue(HeaderProperty, value);
}
}
public PageHeaderControl()
{
InitializeComponent();
}
}
ViewModel.cs:
public class ViewModel : INotifyPropertyChanged
{
private string _pageHeader = "This is my page header";
public string PageHeader
{
get
{
return _pageHeader;
}
set
{
_pageHeader = value;
PropertyChanged(this, new PropertyChangedEventArgs("PageHeader"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
MainPage.xaml:
<Grid x:Name="LayoutRoot" Background="White">
<my:PageHeaderControl Header="{Binding PageHeader, Mode=TwoWay}"></my:PageHeaderControl>
</Grid>
MainPage.xaml.cs:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
I'm a little bit confused and I think you missed the syntax of Binding inline expression.
after "{Binding" comes Path to your property. Is "PageHeader" is a path to your property?!
I think you mean this:
<my:PageHeader Header="{Binding PageHeader, Mode=TwoWay}" />
<TextBlock Text="{Binding PageHeader, Mode=TwoWay}" />
The problem is that Binding expression only works when you set the value of property using SetValue method and notify the parent DependencyObject that specific property has changed!
You should use a DependencyProperty to have TwoWay Binding on it, OR implement System.ComponentModel.INotifyPropertyChange interface in your class and notify the Binding object manually by calling PropertyChanged event in the interface.
The definition of PageHeader property should be like this:
public static readonly DependencyProperty PageHeaderProperty = DependencyProperty.Register("PageHeader", typeof(string), typeof(YOUROWNER), new PropertyMetadata(""));
public string PageHeader
{
get { return GetValue(PageHeaderProperty) as string; }
set { SetValue(PageHeaderProperty, value); }
}
Cheers