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.
Related
I am trying to create MDI kind of functionality whereby I want to load a user control corresponding to the button clicked by user and unload the rest. Every button is associated with a userControl
<Button Content="Worker registration"/> //UserControl1
<Button Content="Worker recognition"/> //UserControl2 ...and so on
<Grid x:Name="UserControlManager"/>
Any reason not to use a tabcontrol? Like this
<TabControl>
<TabItem Header="Control A">
<local:ControlA/>
</TabItem>
<TabItem Header="Control B">
<local:UserControlB/>
</TabItem>
</TabControl>
Or bind all items using the ItemsSource
<TabControl ItemsSource="{Binding MyItems}"/>
There are also third party TabControls that's quite nice, like the one devcomponents provides.
If a TabControl does not suffice (tons of issues I know), you could use a IValueConverter that would convert some property to a view. You could use a Mediator and/or ViewModelLocator, I love MVVM Light from Galasoft. They provide everything through nuget, and even sets up everything for you :)
Add a command for your buttons for selecting the content you want to show. And add the xaml for showing the SelectedControl.
Bad mediator / ViewmodelLocator ;) Use I.E. Galasofts instead like in this post
public class ViewModelLocator : INotifyPropertyChanged
{
private UserControl selectedControl;
private ObservableCollection<UserControl> controls = new ObservableCollection<UserControl>();
public UserControl SelectedControl
{
get { return selectedControl; }
set
{
if (Equals(selectedControl, value)) return;
selectedControl = value;
OnPropertyChanged();
}
}
public ObservableCollection<UserControl> Controls
{
get { return controls; }
set
{
if (Equals(controls, value)) return;
controls = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Hope it helps!
Cheers
Stian
You can use DataTemplates to load views depending on what data (viweModel) you set
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type viewModel:ViewModel1}">
<view:View1 />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:ViewModel2}">
<view:View2 />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
Then have a ContentControl where your content will show
<Grid >
<ContentControl Content="{Binding MyContent}" />
</Grid
Use an enumBooleanConverter (How to bind RadioButtons to an enum?) to select a enum with radiobuttons
<RadioButton GroupName="Navigation"
IsChecked="{Binding Path=SelectedNavigationEnum,
Converter={StaticResource enumBooleanConverter},
ConverterParameter={x:Static viewModel:NavigationEnum.EnumValue1},
Mode=TwoWay}">Show View1</RadioButton>
<RadioButton GroupName="Navigation"
IsChecked="{Binding Path=SelectedNavigationEnum,
Converter={StaticResource enumBooleanConverter},
ConverterParameter={x:Static viewModel:NavigationEnum.EnumValue2},
Mode=TwoWay}">Show View2</RadioButton>
When the SelectedNavigationEnum property is changed set the MyContent property to the selected viewModel
public NavigationEnum SelectedNavigationEnum
{
...
set
{
...
Navigate(value);
}
}
protected void Navigate(NavigationEnum part)
{
switch (part)
{
case NavigationEnum.EnumValue1:
ShowView1();
break;
case NavigationEnum.EnumValue2:
ShowView2();
...
}
}
private void ShowView1()
{
ViewModel1 viewModel = ObjectFactory.GetInstance<ViewModel1>();
MyContent = viewModel;
}
When you set MyContent the DataTemplate will load View1 and set the viewModel as its DataContext.
Lets say I have a MainWindow and in it one Grid Column where i place my UserControl.
And the user can switch the usercontrol in this column with a button click, a tab or a menuItem.
I have 3 userControls : UserControl1, UserControl2 , USerControl3
3 ViewModels : UserControl1ViewModel, UserControl2ViewModel, UserControl3ViewModel
a MainWindow and a MainWindowViewModel
Lets say that in this column the default userControl is the UserControl1. How do I switch it with a Button click to UserControl2.
I found some resources like this :
<Window.Resources>
<DataTemplate DataType="{x:Type vm:UserControl1ViewModel}">
<v:UserControl1 />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:UserControl2ViewModel}">
<v:UserControl1 />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:UserControl3ViewModel}">
<v:UserControl3/>
</DataTemplate>
</Window.Resources>
The idea is to somehow bind a Command to Button or MenuItem to switch the UserControls.
That code is going to be handled in MainWindowViewModel.
In your MainViewModel create a property to hold the displayed ViewModel as follows
private Object _DisplayedViewModel;
public Object DisplayedViewModel
{
get { return _DisplayedViewModel; }
set
{
_DisplayedViewModel = value;
// Your INotifyPropertyChanged notification
//RaisePropertyChanged("DisplayedViewModel");
}
}
In MainWindow.xaml, bind DisplayedViewModel to the frame content.
<Frame Content="{Binding DisplayedViewModel}" NavigationUIVisibility="Hidden"/>
For the button command binding
private ICommand _ShowUC2;
public ICommand ShowUC2
{
get {
if (_ShowUC2 == null)
{
_ShowUC2 = new RelayCommand() =>
{
DisplayedViewModel = new UserControl2ViewModel();
};
}
return _ShowUC2; }
}
Assuming your VMs are wired properly, setting the DisplayedViewModel to any of the three UserControlViewModel reference will cause the respective UserControl to be displayed in the frame.
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; }
}
I want to use MVVM approach to achieve something like below:
I have a MainWindow where i have a 3 Buttons like: a)Customers b) Orders c) Sales
By clicking on button, it should open its respective window/usercontrol xaml with customers details,orders details,sales details.
I have tried everything but culdnt able to do so.
How to achieve this using MVVM pattern. Kindly provide the solution?
Thanks
The answer depends on how you want your Customers, Orders and Sales views displayed. If you want them displayed in the same view, simply add a content control bound to a property in your main ViewModel.
For example, if you're using the MVVM Light Toolkit, your MainPage.xaml might look like...
<UserControl x:Class="MvvmLight2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="300"
Width="300"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button Content="Customers" Command="{Binding DisplayView}" CommandParameter="Customers" Margin="10" />
<Button Content="Orders" Command="{Binding DisplayView}" CommandParameter="Orders" Margin="10" />
<Button Content="Sales" Command="{Binding DisplayView}" CommandParameter="Sales" Margin="10" />
</StackPanel>
<ContentControl Content="{Binding CurrentView}" IsTabStop="False" Margin="10" />
</StackPanel>
</UserControl>
And your MainPageViewModel would be...
using System.Windows.Controls;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace MvvmLight2.ViewModel
{
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
DisplayView = new RelayCommand<string>(DisplayViewCommandExecute);
}
#region Commands
public RelayCommand<string> DisplayView { get; private set; }
#endregion
#region CurrentView Property
public const string CurrentViewPropertyName = "CurrentView";
private UserControl _currentView;
public UserControl CurrentView
{
get { return _currentView; }
set
{
if (_currentView == value)
return;
_currentView = value;
RaisePropertyChanged(CurrentViewPropertyName);
}
}
#endregion
private void DisplayViewCommandExecute(string viewName)
{
switch (viewName)
{
case "Customers":
CurrentView = new CustomersView();
break;
case "Orders":
CurrentView = new OrdersView();
break;
case "Sales":
CurrentView = new SalesView();
break;
}
}
}
}
This all assumes that you have created views and view models for Customers, Orders, and Sales, and modified the ViewModelLocator to include them.
At this point, if you need to display specific information in your child views, you can create a dependency property in them, and set that from your MainViewModel before you display the view.
You may want to look into the mediator pattern . Common implementations are the Messenger class in the MVVM Light Toolkit and Event Aggregation in PRISM.
One basic workflow using this pattern... Command is called on viewmodel1. Viewmodel1 registers some message with the mediator. Viewmodel2 subscribes to that message and does something in response (like creates new view2 or changes visual state of the view2).
I tried this using Sliverlight Naviagtion Application and MVVM
http://garfoot.com/blog/2010/09/silverlight-navigation-with-the-mvvm-pattern/
Pretty simple example. No frameworks involved as such.
But using a MVVM framework makes life easier for future use.
For MVVM and Prism framework check this link..
http://blog.roboblob.com/2010/10/24/introducing-prism-navigation-framework-for-silverlight-mvvm-applications/
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}" />