I am newbie in XAML Databinding and I am stuck in this situation. I am using Mahapps MetroWindow.
Suppose that I have a UserControl named usrctrl_Camera_Control. I have a simple button there. The C# code is given below.
namespace TA141501005
{
public partial class usrctrl_Camera_Control : UserControl
{
public usrctrl_Camera_Control()
{
this.DataContext = this;
InitializeComponent();
}
}
The XAML is given below
<UserControl
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:Custom="http://metro.mahapps.com/winfx/xaml/controls" xmlns:local="clr-namespace:TA141501005" x:Class="TA141501005.usrctrl_Camera_Control"
mc:Ignorable="d"
d:DesignHeight="768" d:DesignWidth="1366" Background="#FF2B2B2B" Height="738" Width="1336">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Icons.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="btn_Test" Content="Button" Grid.Column="1" HorizontalAlignment="Left" Margin="472,59,0,0" VerticalAlignment="Top" Width="75" Grid.RowSpan="2" IsEnabled="{Binding Path=IsNyetEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" />
</Grid>
The C# code for MainWindow is given below.
namespace TA141501005
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : MetroWindow
{
usrctrl_Camera_Control usrctrl_camera_control;
public bool IsNyetEnabled { get; set; }
public MainWindow()
{
IsNyetEnabled = false;
this.DataContext = this;
InitializeComponent();
usrctrl_camera_control = new usrctrl_Camera_Control();
}
}
}
and the XAML code for MainWindow is given below.
<Controls:MetroWindow x:Class="TA141501005.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="Macromium System Control V1.0ES - TA141501005 [Engineering Sample]" Height="768" Width="1366" Background="#FF2B2B2B" ScrollViewer.VerticalScrollBarVisibility="Disabled" ResizeMode="NoResize" WindowStyle="ThreeDBorderWindow" WindowStartupLocation="CenterScreen" IsMinButtonEnabled="False" IsWindowDraggable="False" ShowMaxRestoreButton="False" ShowMinButton="False" ShowSystemMenuOnRightClick="False" IconOverlayBehavior="Flyouts">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Icons.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button x:Name="btn_test" Content="Button" HorizontalAlignment="Left" Margin="589,590,0,0" VerticalAlignment="Top" Width="75" IsEnabled="{Binding IsNyetEnabled}"/>
</Grid>
I want to binding the property IsEnabled in btn_test in usrctrl_Camera_Control and btn_test in MainWindow into IsNyetEnabled in MainWindow. I set the IsNyetEnabled into false before I do InitializeComponent() in MainWindow.
The binding between btn_test.IsEnabled in MainWindow into IsNyetEnabled in MainWindow is done flawlessly. The btn_test in MainWindow is no longer enabled. (I know, I need to implement INotifyPropertyChanged to notify the subsriber if there is any change but for now, just leave it as is for simplicity).
But, the binding between btn_test.IsEnabled in usrctrl_Camera_Control into IsNyetEnabled in MainWindow is failed. I have used Visual Studio "Create Data Binding" wizard but it always return error when compiling.
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='TA141501005.MainWindow', AncestorLevel='1''. BindingExpression:Path=IsNyetEnabled; DataItem=null; target element is 'Button' (Name='btn_Test'); target property is 'IsEnabled' (type 'Boolean')
Do you have any suggestion? I have tried for whole day without a luck.
Is there any way to access parent datacontext without removing this.Datacontext = this?
Looking forward for your suggestion and explanation.
Thank you very much.
Edit.
I display my UserControl via Flyout.
Window parentWindow = Window.GetWindow(this);
object obj = parentWindow.FindName("mainFlyout");
Flyout flyout = (Flyout) obj;
flyout.Content = new SomeFlyOutUserControl();
flyout.IsOpen = !flyout.IsOpen;
Create the IsNyetEnabled property as a dependency property on the MainForm. This is the mechanism that will propagate changes in WPF
Example:
public static readonly DependencyProperty IsNyetEnabledProperty =
DependencyProperty.Register("IsNyetEnabled", typeof(bool),
typeof(MainWindow),new FrameworkPropertyMetadata(false));
public bool IsNyetEnabled
{
get { return (bool)GetValue(MainWindow.IsNyetEnabled); }
set { SetValue(MainWindow.IsNyetEnabled, value); }
}
To that you can bind your Button in the MainWindow directly.
For the UserControl though we have to add a shim, because the UserControl won't be able to find the ancestor when you create it.
Create on your UserControl like Moez posted another DependencyProperty:
public static readonly DependencyProperty IsButtonEnabledProperty =
DependencyProperty.Register("IsButtonEnabled", typeof(bool),
typeof(usrctrl_Camera_Control),new FrameworkPropertyMetadata(false));
public bool IsButtonEnabled
{
get { return (bool)GetValue(usrctrl_Camera_Control.IsButtonEnabledProperty); }
set { SetValue(usrctrl_Camera_Control.IsButtonEnabledProperty, value); }
}
Bind your UserControl button IsEnabled property to that.
Now as a last step we will have to connect the two Dependency Properties when you create your UserControl.
<local:usrctrl_Camera_Control x:Name="yourControl" ...Whatever else... IsButtonEnabled="{Binding Path=IsNyetEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}} />
This should work now.
I suggest you use DependencyProperty :
public static readonly DependencyProperty IsButtonEnabledProperty = DependencyProperty.Register(
"IsButtonEnabled", typeof(bool), typeof(UserControl1), new PropertyMetadata(default(bool)));
public bool IsButtonEnabled
{
get { return (bool)GetValue(IsButtonEnabledProperty); }
set { SetValue(IsButtonEnabledProperty, value); }
}
// Here Rename your UserControl To Test
<Grid>
<Button x:Name="btn_Test" Content="Button" Grid.Column="1" HorizontalAlignment="Left" Margin="472,59,0,0" VerticalAlignment="Top" Width="75" Grid.RowSpan="2" IsEnabled="{Binding Path=IsButtonEnabled, ElementName=Test}"}" />
</Grid>
As #Frank J suggested, I modify his last step.
I create the usercontrol during runtime, not from the XAML. Therefore it would be look like this for the last step.
usrctrl_camera_control = new usrctrl_Camera_Control();
Binding b = new Binding("IsNyetEnabled");
b.Source = this;
b.Mode = BindingMode.OneWay;
usrctrl_camera_control.SetBinding(usrctrl_Camera_Control.IsButtonEnabledProperty, b);
Thanks all.. I hope it would be useful for those who have the same problem with me.
Related
I get this error:- System.NullReferenceException: 'Object reference not set to an instance of an object.'
objectPlacement was null.
private void Button_Click(object sender, RoutedEventArgs e)
{
ObjectPlacement w = new ObjectPlacement() {Topmost = };// ObjectPlacement is new WPF window
objectPlacement.WindowStyle = WindowStyle.None;
settingpanel.Children.Add(objectPlacement);//settingpanel stack is panel name
w.Show();
}
It would be much more usual to define a usercontrol or datatemplate for whatever you're trying to show in your window. A window is a kind of content control. One way to think of a window ( or contentcontrol ) is something that shows you some UI. All the UI in a window is that content.
When you add window to a project it is templated out with a grid in it.
This is the content and everything you want to see in that window goes in it.
You could replace that grid with something else instead.
If you made that a contentpresenter then you can bind or set what that'll show to some encapsulated re-usable UI.
Usually the best way to encapsulate re-usable UI is as a usercontrol.
A datatemplate can reference a usercontrol.
It is not usually your entire UI for a window you want to switch out. But you can and that is occasionally useful - say if you want a generic way to show dialogs.
The usual way to write wpf is mvvm so most devs will want some mvvm way of switching out UI.
I'll show you some code might make the description clearer.
There are some corners cut in what follows, so this is illustrative. Don't just run with this for your next lead developer interview at a stock traders.
But, basically you click a button for Login you "navigate" to a LoginUC view. Click a button for User and you "navigate" to UserUC.
My mainwindow.
<Window.Resources>
<DataTemplate DataType="{x:Type local:LoginViewModel}">
<local:LoginUC/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:UserViewModel}">
<local:UserUC/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ItemsControl ItemsSource="{Binding NavigationViewModelTypes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding VMType}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ContentPresenter Grid.Column="1"
Content="{Binding CurrentViewModel}"
/>
</Grid>
</Window>
Notice the datatemplates which associate the type of a viewmodel with a usercontrol.
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/data-templating-overview?view=netframeworkdesktop-4.8
What will happen is you present your data in a viewmodel to the UI via that contentpresenter and binding. That viewodel is then templated out into UI with your viewmodel as it's datacontext. The datacontext of a UserUC view will therefore be an instance of UserViewModel. Change CurrentViewModel to an instance of LoginViewModel and you get a LoginUC in your mainwindow instead.
The main viewmodel.
public class MainWindowViewModel : INotifyPropertyChanged
{
public string MainWinVMString { get; set; } = "Hello from MainWindoViewModel";
public ObservableCollection<TypeAndDisplay> NavigationViewModelTypes { get; set; } = new ObservableCollection<TypeAndDisplay>
(
new List<TypeAndDisplay>
{
new TypeAndDisplay{ Name="Log In", VMType= typeof(LoginViewModel) },
new TypeAndDisplay{ Name="User", VMType= typeof(UserViewModel) }
}
);
private object currentViewModel;
public object CurrentViewModel
{
get { return currentViewModel; }
set { currentViewModel = value; RaisePropertyChanged(); }
}
private RelayCommand<Type> navigateCommand;
public RelayCommand<Type> NavigateCommand
{
get
{
return navigateCommand
?? (navigateCommand = new RelayCommand<Type>(
vmType =>
{
CurrentViewModel = null;
CurrentViewModel = Activator.CreateInstance(vmType);
}));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Type and display relates the type for a viewmodel with text displayed in the UI.
public class TypeAndDisplay
{
public string Name { get; set; }
public Type VMType { get; set; }
}
This is "just" quick and dirty code to illustrate a principle which is usually called viewmodel first navigation. Google it, you should find a number of articles explaining it further.
For completeness:
<UserControl x:Class="wpf_Navigation_ViewModelFirst.LoginUC"
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:wpf_Navigation_ViewModelFirst"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Background="Yellow">
<TextBlock Text="This is the Login User Control"/>
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding LoginCommand}"/>
</TextBox.InputBindings>
</TextBox>
</StackPanel>
</UserControl>
public class LoginViewModel
{
private RelayCommand loginCommand;
public RelayCommand LoginCommand
{
get
{
return loginCommand
?? (loginCommand = new RelayCommand(
() =>
{
string s = "";
}));
}
}
}
<UserControl x:Class="wpf_Navigation_ViewModelFirst.UserUC"
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:wpf_Navigation_ViewModelFirst"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="pink">
<TextBlock Text="This is the User module Control"
VerticalAlignment="Top"
/>
<TextBlock Text="{Binding Path=DataContext.MainWinVMString, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
VerticalAlignment="Bottom"
/>
</Grid>
</UserControl>
public class UserViewModel
{
}
I put this together some years ago, I would now recommend the community mvvm toolkit with it's code generation, base classes, messenger etc.
I'm using ViewModel-first aprroach. In MainWindow.xaml I've set ContentControl where I display my UserControls (Views), by a click on a MenuItem. When I click to display UserControl first time, everything works fine.
But when I click same MenuItem to open It once again, my UserControl displays again but doesn't get loaded anymore, resulting in not having refreshed bindings. Setting my ContentControl's Content to null doesn't resolve issue.
My whole setup is like this:
1.) App.xaml resource
<!--DataContext for MainWindow.xaml-->
<ViewModels:MainWindowViewModel x:Key="Main_VM"/>
<!--DataTemplate for UserControl-->
<DataTemplate DataType="{x:Type ViewModels:MyViewModel}">
<Views:MyView />
</DataTemplate>
2.) MainWindow.xaml, where my ContenControl is located
<Window x:Class="My.Views.MainWindowView"
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"
DataContext="{StaticResource Main_VM}">
<Grid>
<!--Menu which opens view on command-->
<Menu VerticalAlignment="Top" IsMainMenu="True" >
<MenuItem Header="My View" Command="{Binding Show_View}" CommandParameter="1"/>
</Menu>
<ContentControl Content="{Binding Display_View}" />
<!--And all other controls, like Menu for opening views on click...-->
</Grid>
</Window>
3.) ViewModel for Mainwindow.xaml (inherited from BaseViewModel)
public class MainWindowViewModel : BaseViewModel
{
public MainWindowViewModel()
{
//Command for displaying Views
Show_View = new Relay_Command(Open_view, null);
}
public ICommand Show_View { get; set; }
private BaseViewModel _display_view;
public BaseViewModel Display_View
{
get { return _display_view; }
set { _display_view = value; OnPropertyChanged(); }
}
private void Open_view(object parameter)
{
Display_View = null; //This doesn't help at all!!!
switch (parameter)
{
case "1":
Display_View= new MyViewModel();
break;
}
}
}
4.) And my UserControl.xaml
<UserControl x:Class="MyProject.Views.MyView"
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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<!--Event-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<!--Calling a method on Load (firing only first tme !!)-->
<ei:CallMethodAction MethodName="MethodForRetrievingData" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<!--Controls in UserControl for binding etc...-->
</Grid>
</UserControl>
I've tried debugging, but as told, Loaded event of UserControl happens only once. I'm out of ideas on this one, looks like my design has a flaw.
What could be a problem here, maybe I'm missing something like NotifyProperty on UserControl itself?
You need to actually unload the view for it to be loaded again. Setting the source property of the ContentControl's Content property to null just before setting it to another MyViewModel won't unload the view. The DataTemplate is "cached".
Why don't you call the MethodForRetrievingData from the view model itself instead of relying on the view raising a Loaded event? You may for example initialize it asynchronously.
Instead of displaying a MyViewModel object content using template, try display a MyView content instead.
So you would have
private MyView _display_view;
public MyView Display_View
{
get { return _display_view; }
set { _display_view = value; OnPropertyChanged(); }
}
private void Open_view(object parameter)
{
Display_View = null; //This doesn't help at all!!!
switch (parameter)
{
case "1":
Display_View= new MyView(); // Or assign a view model here: {DataContext=new MyViewModel()}
break;
}
}
I have a problem with binding in usercontrol.
This is my usercontrol:
UserControl1.xaml
<UserControl x:Class="WpfApp1.UserControl1"
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-ompatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
x:Name="usercontrol"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBox Text="{Binding HmiField, ElementName=usercontrol}"/>
</Grid>
</UserControl>
UserControl1.xaml.cs
namespace WpfApp1
{
public partial class UserControl1 : UserControl
{
public double HmiField
{
get { return (double)GetValue(HmiFieldProperty); }
set { SetValue(HmiFieldProperty, value); }
}
public static readonly DependencyProperty HmiFieldProperty =
DependencyProperty.Register("HmiField", typeof(double), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
}
}
And this is the main window:
MainWindow.xaml
<Window x:Class="WpfApp1.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:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
DataContext="{Binding Md, RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="450" Width="800">
<UniformGrid>
<Button Content="{Binding Prop1}" Click="Button_Click"/>
<Label Content="{Binding Prop1}"/>
<TextBox Text="{Binding Prop1}"/>
<local:UserControl1 HmiField="{Binding Prop1}"/>
</UniformGrid>
</Window>
MainWindow.xaml.cs
namespace WpfApp1
{
public class tMd: INotifyPropertyChanged
{
#region Interfaccia INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
#endregion
private double prop1;
public double Prop1 { get
{
return prop1;
}
set
{
if (prop1 != value)
{
prop1 = value;
NotifyPropertyChanged("Prop1");
}
}
}
}
public partial class MainWindow : Window
{
public tMd Md
{
get { return (tMd)GetValue(MdProperty); }
set { SetValue(MdProperty, value); }
}
public static readonly DependencyProperty MdProperty =
DependencyProperty.Register("Md", typeof(tMd), typeof(MainWindow), new PropertyMetadata(new tMd()));
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Md.Prop1 = 1234.5678;
}
}
}
I found some similar question:
How do I change TextBox.Text without losing the binding in WPF?
WPF: Binding is lost when bindings are updated
WPF Textbox TwoWay binding in datatemplate not updating the source even on LostFocus
But I can't completely understand what's happening: why a standard textbox work as expected and my usercontrol no?
Or better: is there a way to have my usercontrol works with the textbox's behaviour?
The Binding must be TwoWay, either set explicitly
<local:UserControl1 HmiField="{Binding Prop1, Mode=TwoWay}"/>
or implicitly by default:
public static readonly DependencyProperty HmiFieldProperty =
DependencyProperty.Register(
nameof(HmiField), typeof(double), typeof(UserControl1),
new FrameworkPropertyMetadata(
0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
A TextBox's Text property is registered like shown above, i.e. with the BindsTwoWayByDefault flag.
At the TextBox Binding in the UserControl's XAML you may also want to update the source property while the user is typing (instead of only on lost focus):
<TextBox Text="{Binding HmiField,
ElementName=usercontrol,
UpdateSourceTrigger=PropertyChanged}"/>
or without the otherwise useless generated usercontrol field:
<TextBox Text="{Binding HmiField,
RelativeSource={RelativeSource AncestorType=UserControl}
UpdateSourceTrigger=PropertyChanged}"/>
Your Prop1 notifies when it changes, but you haven't told you binding to trigger on that notification.
Try including UpdateSourceTrigger=PropertyChanged in you binding
I am creating a UserControl for a series of controls shared by several windows. One of the controls is a Label which shows the flow of some other process in terms of "protocol numbers".
I am trying to offer DataBinding with this Label so the Window automatically reflects the state of the process as the protocol number variable changes.
This is the User control XAML:
<UserControl Name="MainOptionsPanel"
x:Class="ExperienceMainControls.MainControls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<Label Height="Auto" Name="numberLabel">Protocol:</Label>
<Label Content="{Binding Path=ProtocolNumber}" Name="protocolNumberLabel"/>
(...)
</UserControl>
And this is the Code-Behind:
public partial class MainControls
{
public MainControls()
{
InitializeComponent();
}
public int ProtocolNumber
{
get { return (int)GetValue(ProtocolNumberProperty); }
set { SetValue(ProtocolNumberProperty, value); }
}
public static DependencyProperty ProtocolNumberProperty =
DependencyProperty.Register("ProtocolNumber", typeof(int), typeof(MainControls));
}
This seems to be working because if on the constructor I set ProtocolNumber to an arbitrary value, it is reflected in the user control.
However, when using this usercontrol on the final window, the data binding breaks.
XAML:
<Window x:Class="UserControlTesting.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:expControl="clr-namespace:ExperienceMainControls;assembly=ExperienceMainControls"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<StackPanel>
<expControl:MainControls ProtocolNumber="{Binding Path=Number, Mode=TwoWay}" />
</StackPanel>
</Window>
Code-Behind for window:
public partial class Window1 : Window
{
public Window1()
{
Number= 15;
InitializeComponent();
}
public int Number { get; set; }
}
This sets the Protocol Number to zero, ignoring the value set to Number.
I've read example
if you look at your output window you should see the binding exception.
The problem you have is the following: within your usercontrol you will bind the label to the DP ProtocolNumber of your usercontrol and not the DataContext, so you have to add for example the element name to the binding.
<UserControl Name="MainOptionsPanel"
x:Class="ExperienceMainControls.MainControls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="uc"
>
<Label Height="Auto" Name="numberLabel">Protocol:</Label>
<Label Content="{Binding Path=ProtocolNumber, ElementName=uc}" Name="protocolNumberLabel"/>
(...)
</UserControl>
EDIT: to clear some things up, your usercontrol also works if you change the binding in your MainWindow. but you have to bind to the DataContext of the MainWindow with RelativeSource.
<expControl:MainControls ProtocolNumber="{Binding Path=Number, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
What you have in effect:
<expControl:MainControls DataContext="{Binding RelativeSource={RelativeSource Self}}"
ProtocolNumber="{Binding Path=Number, Mode=TwoWay}"/>
=> Do not set the DataContext in UserControl declarations, use RelativeSource or ElementName bindings instead.
If you're not specifying the RelativeSource of the binding, try set the DataContext in the constructor:
public Window1()
{
Number= 15;
DataContext = this;
InitializeComponent();
}
I am having a hard time understanding the CommandTarget property for a RoutedCommand.
Basically, I have some static commands that have implementations in a user control (not the window). I create a commandbinding in the user control. If I declare the button in the usercontrol, then I am able to use my routed event. However, when the button is outside of the usercontrol, then I cannot use my routed event. I think the command target will solve my issue.
So how do I set the commandtarget for the toolbar usercontrol's button, so that the Container's Executed and CanExecuted is called?
Edited Code with changes from micahtan changes, but I still can't get it to CanExecute or Execute.
Window XAML:
<Window x:Class="RoutedCommands.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RoutedCommands"
xmlns:toolbar="clr-namespace:RoutedCommands.Toolbar"
Title="Window1" Height="300" Width="300">
<StackPanel>
<local:Container Width="100" Height="25" x:Name="MyContainer" />
<toolbar:Toolbar Width="100" Height="25" CommandTarget="{Binding MyContainer}" />
</StackPanel>
</Window>
Toolbar XAML:
<UserControl x:Class="RoutedCommands.Toolbar.Toolbar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RoutedCommands"
x:Name="MyToolbar"
Height="300" Width="300">
<Grid>
<Button Command="{x:Static local:Commands.MyCommand}" Content="Try Me" CommandTarget="{Binding ElementName=MyToolbar, Path=CommandTarget, Mode=OneWay}" />
</Grid>
</UserControl>
Toolbar CS:
public partial class Toolbar : UserControl
{
public Toolbar()
{
InitializeComponent();
}
// Using a DependencyProperty as the backing store for CommandTarget. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(Toolbar), new UIPropertyMetadata(null));
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
}
Container XAML:
<UserControl x:Class="RoutedCommands.Container"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RoutedCommands"
Height="300" Width="300">
<UserControl.CommandBindings>
<CommandBinding Command="{x:Static local:Commands.MyCommand}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed" />
</UserControl.CommandBindings>
<Grid>
<Button Command="{x:Static local:Commands.MyCommand}" Content="Click Me" />
</Grid>
</UserControl>
Container CS:
public partial class Container : UserControl
{
public Container()
{
InitializeComponent();
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
Console.WriteLine("My Command Executed");
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
Console.WriteLine("My Command Can Execute");
e.CanExecute = true;
}
}
RoutedCommands:
namespace RoutedCommands
{
public static class Commands
{
public static readonly RoutedUICommand MyCommand = new RoutedUICommand();
}
}
If you want to use CommandTargets, I would create a CommandTarget DependencyProperty on your custom UserControl, similar to the way it's defined on ButtonBase.
After doing that, set your Button's CommandTarget to your custom UserControl's CommandTarget.
EDIT: Code Sample
Rudi's comments are valid if you're doing an MVVM architecture -- RelayCommands or some other form of wrapped delegates work well in that case. Based on your code sample, it didn't look like you were using that approach, hence my original comment.
As for the code, you only need to change your ToolBar class. This assumes your MyCommand class inherits from RoutedUICommand. Here's the XAML:
<UserControl
x:Class="WPFCommandTarget.CustomToolBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFCommandTarget"
x:Name="theControl">
<Grid>
<Button
x:Name="theButton"
Command="{x:Static local:Commands.MyCommand}"
CommandTarget="{Binding ElementName=theControl, Path=CommandTarget, Mode=OneWay}"
Content="Try Me" />
</Grid>
</UserControl>
And here's the code-behind:
using System.Windows;
using System.Windows.Controls;
namespace WPFCommandTarget
{
/// <summary>
/// Interaction logic for CustomToolBar.xaml
/// </summary>
public partial class CustomToolBar : UserControl
{
public CustomToolBar()
{
InitializeComponent();
}
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
// Using a DependencyProperty as the backing store for CommandTarget. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(CustomToolBar), new UIPropertyMetadata(null));
}
}
Please note that I've changed some of the class names/namespaces in my test project. You'll have to change them to suit your needs.
Have you concidered to rather use the RelayCommand or DelegateCommand! Thy might be more suited to what you need?
For a example of using RelayCommand, read this article by Josh
Brian Noyes also have a excellent article available here