WPF UserControls - setting the .Command property on button inside UserControl - wpf

I've got a UserControl that contains a button and some other controls:
<UserControl>
<StackPanel>
<Button x:Name="button" />
...
</StackPanel>
</UserControl>
When I create a new instance of that control, I want to get at the Button's Command property:
<my:GreatUserControl TheButton.Command="{Binding SomeCommandHere}">
</my:GreatUserControl>
Of course, the "TheButton.Command" thing doesn't work.
So my question is: Using XAML, how can I set the .Command property of the button inside my user control?

Add a dependency property to your UserControl and bind the button's Command property to that.
So in your GreatUserControl:
public ICommand SomeCommand
{
get { return (ICommand)GetValue(SomeCommandProperty); }
set { SetValue(SomeCommandProperty, value); }
}
public static readonly DependencyProperty SomeCommandProperty =
DependencyProperty.Register("SomeCommand", typeof(ICommand), typeof(GreatUserControl), new UIPropertyMetadata(null));
And in your GreatUserControl's XAML:
<UserControl
x:Class="Whatever.GreatUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="me"
>
<Button Command="{Binding SomeCommand,ElementName=me}">Click Me!</Button>
</UserControl>
So your button binds to the command on the UserControl itself. Now you can set that in your parent window:
<my:GreatUserControl SomeCommand="{Binding SomeCommandHere}" />

Related

Bind button from user control to viewmodel doesn't work as expected

I Have MainWindow.xaml and MainWindowViewModel, I have User Conterol inside MainWindow.xaml, I want when user clicks User control button to send this event to MainWindowViewModel, I have:
Inside main window I have:
<Controls:UserControl1 CloseBtn="{Binding CloseBtn}" ></Controls:UserControl1>
UserControl1.xaml:
<Button Command="{Binding CloseBtn}" />
UserControl1.cs:
public static readonly DependencyProperty CloseProperty =
DependencyProperty.Register(
"CloseBtn",
typeof(ICommand),
typeof(UserControl1),
new PropertyMetadata(null));
public ICommand CloseBtn
{
get { return (ICommand)GetValue(CloseProperty); }
set { SetValue(CloseProperty, value); }
}
MainWindowViewModel.cs:
public ICommand CloseBtn { get; set; }
public MainWindowViewModel()
{
CloseBtn = new RelayCommand(o => BtnCloseSettings());
}
void BtnCloseSettings()
{
MessageBox.Show("test");
}
The MainWindow and the viewmodel are connected, but this button click doesn't popup the "test" messageBox.
what am I missing?
The problem is this line here:
<Button Command="{Binding CloseBtn}" />
You've create a dependency property in UserControl1, which you are correctly binding with this line:
<Controls:UserControl1 CloseBtn="{Binding CloseBtn}" ></Controls:UserControl1>
But that first binding is binding to the CloseBtn property of the UserControl's DataContext. It needs to bind to the UserControl's CloseBtn dependency property instead. To fix this, start by giving your UserControl a name:
<UserControl x:Class="YourApp.UserControl1"
... etc ...
x:Name="_this">
And then change your button command binding to bind to that instead:
<Button Command="{Binding CloseBtn, ElementName=_this}" />
Or, as mentioned in a comment:
<Button Command="{Binding CloseBtn,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
where you wouldn't need to assign the UserControl's x:Name property, and thus avoid the creation of an otherwise unused private field.

How to show floating virtual keyboard (user control) in MainWindow when an input control (from another user control) has been set to focus in WPF?

I have been doing development work in WPF application which uses an MVVM pattern for a couple of days now. I'm very new to WPF and MVVM pattern as well.
In my scenario, I have a user control view (named EPayView.xaml) which has a textbox that will accept a phone number. The view has a corresponding viewmodel (named EPayViewModel.cs). In the MainWindow.xaml, I have a user control (floating virtual keyboard) which is derived from namespace controls WpfKb.Controls. The MainWindow.xaml also has a corresponding viewmodel (named MainViewModel.cs)
Having said that, I have done research on how to use attached dependency properties which lead me to this solution. Set focus on textbox in WPF from view model (C#) which I believe this is where I could bind the property IsFocused in the textbox of EPayView.xaml.
Below are the codes that I have already incorporated in my solution.
EpayView.xaml (textbox xaml markup)
<TextBox Text="{Binding PhoneNo}" Grid.Row="5" Margin="10,0,10,0" VerticalContentAlignment="Center" FontSize="12" x:Name="Email" behaviors:FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"/>
MainWindow.xaml (xaml markup)
<Window x:Class="SmartPole540.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb"
xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl"
xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core"
xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core"
xmlns:square="clr-namespace:SmartPole.View.Square;assembly=SmartPole.View"
xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View"
Title="CitiPulse"
WindowStartupLocation="Manual"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
Name="mainWindow">
<userControls:RollPanel.BottomContent>
<square:SquareView Canvas.Top="1010" DataContext="{Binding DataContext.SquareViewModel,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type userControls:RollPanel}}}"/>
</userControls:RollPanel.BottomContent>
<controls:FloatingTouchScreenKeyboard
x:Name="floatKb" Width="500" Height="250" PlacementTarget="{Binding ElementName=MainGrid}"
Placement="Center" AreAnimationsEnabled="False" Visibility="Visible"
IsOpen="{Binding IsChecked, ElementName=kbButton}"/>
</Window>
In the above code, the user control RollPanel.BottomContent host the EPayView.xaml view inside another view which is RollPanel.xaml
EpayViewModel.cs contains the static class FocusExtension for the IsFocused attached property (refer to this solution - Set focus on textbox in WPF from view model (C#)). And, EPayViewModel.cs already implemented INotifyPropertyChanged which is wrapped inside a concrete class ObservableObject that accepts type of T. This is also same with MainViewModel.cs
public class EPayViewModel : ObservableObject<EPayViewModel>, IPaymentViewModel, IActiveViewModel
{ ... }
public class MainViewModel : ObservableObject<MainViewModel>
{ ... }
As such, my goal is that when the textbox in EPayView.xaml has the focus, the floating virtual keyboard (floatKb) in the MainWindow.xaml will be shown.
I'm stuck on how to proceed (I was thinking if a call to FocusExtension static class in EPayViewModel inside my MainViewModel.cs will suffice?), any help is greatly appreciated.
Cheers,
As AnjumSKhan already said, to react to some event in a MVVM way, you'll have to use Command. Command can be called within an EventTrigger, you will need to add a Reference to System.Windows.Interactvity component.
Let's assume you have a simple View and View Model and you need to show this View when the TextBox in a MainWindow got focus.
View (NewWindow.xaml)
<Window x:Class="My.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<TextBlock Text="{Binding Message}"/>
View Model
public class NewWindowViewModel
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; }
}
}
You also have a MainWindow, it is a main view for an app and it contains the target TextBox. You may see that there is an EventTrigger added to the TextBox and it has a property InvokeCommandAction which is binded to the MainWindowViewModel's command called ShowCommand.
Main Window
<Window x:Class="My.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" Title="MainWindow" Height="350" Width="525">
<TextBox Height="40" Text="{Binding Text}">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="GotFocus">
<Interactivity:InvokeCommandAction Command="{Binding ShowCommand}"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</TextBox>
In the Show method of MainWindowViewModel NewWindow view is created and got new NewWindowViewModel instance as a DataContext. RelayCommand class is presented in my answer to this question
MainWindowViewModel
public class MainWindowViewModel
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; }
}
private ICommand _increaseCommand;
public ICommand ShowCommand
{
get
{
if (_increaseCommand == null)
{
_increaseCommand = new RelayCommand(
p => true,
Show);
}
return _increaseCommand;
}
}
private void Show(object obj)
{
var w = new NewWindow();
var nvm = new NewWindowViewModel();
nvm.Message = "Test";
w.DataContext = nvm;
w.Show();
}
}
What is left is to create a new MainWindowViewModel and setup a DataContext for MainWindow.
public MainWindow()
{
InitializeComponent();
var mvm = new MainWindowViewModel();
mvm.Text = "Focus me!";
DataContext = mvm;
}
Hope it will help.

WPF: Trigger RoutedCommands implemented in UserControl nested inside a ContentControl

How can I trigger routed commands implemented inside a UserControl which is nested inside a ContentControl?
What I basically have is an outer view (derived from UserControl) which contains:
1) A button which should trigger the command MyCommand:
The CommandTarget is obviously wrong here, as it should be the view which is hosted inside the ContentControl, and not the content control itself, as the CommandBinding is added to the CommandBindings collection of InnerView.
<Button Command="{x:Static Commands:MyCommands.MyCommand}" CommandTarget="{Binding ElementName=ViewHost}">
Trigger Command
</Button>
2) A ContentControl. The Content property is bound to the ViewModel which should be used by the inner view:
<ContentControl x:Name="ViewHost" Content="{Binding InnerViewModel}" />
3) A DataTemplate which defines the type of the inner view:
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type ViewModels:InnerViewModel}">
<Views:InnerView />
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
InnerView (derived from UserControl) sets a CommandBinding in it's Loaded event:
public partial class InnerView : UserControl
{
private void InnerViewLoaded(object sender, System.Windows.RoutedEventArgs e)
{
view.CommandBindings.Add(new CommandBinding(MyCommands.MyCommand, this.ExecuteMyCommand, this.CanExecuteMyCommand));
}
}
And of course a class which defines the command:
internal class MyCommands
{
static MyCommands()
{
MyCommand = new RoutedCommand("MyCommand", typeof(MyCommands));
}
public static RoutedCommand MyCommand { get; private set; }
}
How can I get this working? The problem is probably that the CommandTarget on the Button is wrong. How can I bind the CommandTarget to the control hosted by the ContentControl?
If I put InnerView directly into OuterView and set the Button's CommandTarget to the InnerView instance, it works:
<Views:InnerView x:Name="InnerViewInstance" />
<Button Command="{x:Static Commands:MyCommands.MyCommand}" CommandTarget="{Binding ElementName=InnerViewInstance}">
Trigger Command
</Button>
Try this
<UserControl.Resources>
<ResourceDictionary>
<Views:InnerView x:Key="innerView"/>
<DataTemplate DataType="{x:Type ViewModels:InnerViewModel}">
<ContentControl Content="{StaticResource innerView}" />
</DataTemplate>
</ResourceDictionary>
<Button Command="{x:Static Commands:MyCommands.MyCommand}" CommandTarget="{StaticResource innerView}">
Trigger Command
</Button>
I havent tested it but hope this will help you. though this seems a very complex issue.
I ran into this issue and learned that I had to register a command type dependency property for each user control within my user control hierarchy.
I learned this my another link on this site.

WPF User vs Custom Control

I have a grid control that I use throughout the application. I would like to extend the grid control to include a context menu with one item "freeze/unfreeze columns". If I elect to use a custom control, I cannot implement this functionality within the control -- instead, I have to implement the functionality wherever I place my custom control. The other alternative is user control, in which I can implement all the necessary functionality within the control:
<Grid>
<dxg:GridControl Name="gridData" DataSource="{Binding}" dx:DXSerializer.StoreLayoutMode="All">
<dxg:GridControl.Resources></dxg:GridControl.Resources>
<dxg:GridControl.Columns />
<dxg:GridControl.View>
<dxg:TableView ShowGroupPanel="False" MouseRightButtonUp="TableView_MouseRightButtonUp">
<dxg:TableView.ColumnMenuCustomizations>
<dxb:BarButtonItem Name="freezeColButton" Content="Freeze Column(s)" dxb:BarItemLinkActionBase.ItemLinkIndex="0" ItemClick="freezeColButton_ItemClick" />
</dxg:TableView.ColumnMenuCustomizations>
</dxg:TableView>
</dxg:GridControl.View>
</dxg:GridControl>
</Grid>
Notice, the TableView.ColumnMenuCustomization tag includes the event handler for the freeze/unfreeze functionality.
However, the only issue with the user control is that I cannot access the underlying Grid's Columns property. For example, when I place my user control (defined above) in a window, I get an error (Error 25: The tag 'ExtendedGridControl.Columns' does not exist in XML namespace 'clr-namespace:UI.Controls'):
<Window>
...
<Grid>
<uc:ExtendedGridControl x:Name="extendedGridData" >
<uc:ExtendedGridControl.Columns>
<dxg::GridColumn FieldName="FieldA" Visible="True" />
...
</uc:ExtendedGridControl.Columns>
</uc:ExtendedGridControl>
</Grid
</Window>
How can I expose the GridControl properties? Any help/suggestions would be greatly appreciated.
You need to propagate the properties by defining them on the UserControl, e.g.
public partial class Bogus : UserControl
{
// You often can reuse properties via DependencyProperty.AddOwner
public static readonly DependencyProperty ItemsSourceProperty = ItemsControl.ItemsSourceProperty.AddOwner(typeof(Bogus));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemTemplateProperty = ItemsControl.ItemTemplateProperty.AddOwner(typeof(Bogus));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public Bogus()
{
InitializeComponent();
}
}
<UserControl x:Class="Test.UserControls.Bogus" 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" Name="control">
<StackPanel>
<TextBlock Text="Lorem Ipsum:" />
<ItemsControl ItemsSource="{Binding ElementName=control, Path=ItemsSource}"
ItemTemplate="{Binding ElementName=control, Path=ItemTemplate}" />
</StackPanel>
</UserControl>
The properties are visible outside and the internal controls bind to them.
For some properties you do not use a DependencyProperty, but just a clr-property which references the internal control's property, this may be preferable with certain properties that only have setters or internal constructors or are not dependency properties in the internal controls either, e.g.
public ItemCollection Items
{
get { return _itemsControl.Items; }
}

How to bind to a WPF dependency property when the datacontext of the page is used for other bindings?

How to bind to a WPF dependency property when the datacontext of the page is used for other bindings? (Simple question)
The datacontext of the element needed to be set.
XAML:
<Window x:Class="WpfDependencyPropertyTest.Window1" x:Name="mywindow">
<StackPanel>
<Label Content="{Binding Path=Test, ElementName=mywindow}" />
</StackPanel>
</Window>
C#:
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(string),
typeof(Window1),
new FrameworkPropertyMetadata("Test"));
public string Test
{
get { return (string)this.GetValue(Window1.TestProperty); }
set { this.SetValue(Window1.TestProperty, value); }
}
Also see this related question:
WPF DependencyProperties
In XAML:
Something="{Binding SomethingElse, ElementName=SomeElement}"
In code:
BindingOperations.SetBinding(obj, SomeClass.SomethingProperty, new Binding {
Path = new PropertyPath(SomeElementType.SomethingElseProperty), /* the UI property */
Source = SomeElement /* the UI object */
});
Though usually you will do this the other way round and bind the UI property to the custom dependency property.

Resources