I have a ShellView using interactivity objects with prism's PopupWindowAction for show my custom settings view. And my ShellViewModel contain InteractionRequest object and a Delegate Command that will fire the user interaction. After user fired interaction, custom settings view (DataFeedManagerView) appear center of ShellView. In My DataFeedManagerView, there is a list of DataFeeds (ListBox control) on the left side and there is datafeed specific settings view (ContentControl with set Region via RegionManager) on the right side. First, i registered all Views with RegisterViewWithRegion. Then what i try to do is activate related object settings view within content control via Region's Activate method. When i try to do like this, i'm receiving an error "can not find region". So we can not use regions inside custom popup window???
PS1: Maybe this is so simple requirement but containing many steps because of that my explanation was a little bit complicated. I hope code will be more descriptive.
PS2: I meet expectations with using simple binding to ContentControl's content property. But i'm worry about what is my mistake and/or right solution for use regions inside custom interaction popup window.
..::Shell::..
<Window x:Class="PrismUnityApp.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:views="clr-namespace:PrismUnityApp.Views"
xmlns:constants="clr-namespace:PrismUnityApp.Constants"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="480" Width="640">
<DockPanel LastChildFill="True">
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<views:DataFeedManagerView/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Button Content=" Show Data Feed Manager" Command="{Binding ShowDataFeedManagerCommand}"/>
<ContentControl prism:RegionManager.RegionName="{x:Static constants:WellKnownRegionNames.ContentRegion}" />
</DockPanel>
using System.Windows.Input;
using Prism.Commands;
using Prism.Interactivity.InteractionRequest;
using Prism.Mvvm;
namespace PrismUnityApp.ViewModels
{
public class ShellViewModel : BindableBase
{
private string _title = "Prism Unity Application";
public ICommand ShowDataFeedManagerCommand { get; }
public InteractionRequest<IConfirmation> ConfirmationRequest { get; }
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public ShellViewModel()
{
ConfirmationRequest = new InteractionRequest<IConfirmation>();
ShowDataFeedManagerCommand = new DelegateCommand(ShowDataFeedManager);
}
public void ShowDataFeedManager()
{
ConfirmationRequest.Raise(
new Confirmation {Title = "Data Feed Manager", Content = string.Empty},
confirmation =>
{
});
}
}
}
..::DataFeedManager::..
<UserControl x:Class="PrismUnityApp.Views.DataFeedManagerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:constants="clr-namespace:PrismUnityApp.Constants"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
prism:ViewModelLocator.AutoWireViewModel="True"
Height="240" Width="320">
<DockPanel LastChildFill="True">
<ListBox
SelectedItem="{Binding Current, Mode=OneWay}"
ItemsSource="{Binding DataFeeds}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction
Command="{Binding SelectionChangedCommand}"
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource Self}}"></prism:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ContentControl prism:RegionManager.RegionName="{x:Static constants:WellKnownRegionNames.DataFeedRegion}"></ContentControl>
</DockPanel>
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.Practices.Unity;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using PrismUnityApp.Constants;
using PrismUnityApp.Interfaces;
namespace PrismUnityApp.ViewModels
{
public class DataFeedManagerViewModel : BindableBase, IDataFeedManagerViewModel
{
private readonly IRegionManager _regionManager;
public IDictionary<string, object> DataFeeds { get; }
public ICommand SelectionChangedCommand { get; }
public DataFeedManagerViewModel(IUnityContainer unityContainer, IRegionManager regionManager)
{
_regionManager = regionManager;
SelectionChangedCommand = new DelegateCommand<SelectionChangedEventArgs>(SelectionChanged);
DataFeeds = new Dictionary<string, object>
{
{WellKnownDataFeedNames.SimulationDataFeed, unityContainer.Resolve<ISimulationDataFeedView>()},
{WellKnownDataFeedNames.BarchartDataFeed, unityContainer.Resolve<IBarchartDataFeedView>()}
};
foreach (var dataFeed in DataFeeds)
_regionManager.RegisterViewWithRegion(WellKnownRegionNames.DataFeedRegion, () => dataFeed.Value);
}
public void SelectionChanged(SelectionChangedEventArgs e)
{
var addedItem = (KeyValuePair<string, object>) e.AddedItems[0];
var region = _regionManager.Regions[WellKnownRegionNames.DataFeedRegion];
region.Activate(addedItem.Value);
}
}
}
..::Bootstrapper::..
using System.Windows;
using Microsoft.Practices.Unity;
using Prism.Unity;
using PrismUnityApp.Interfaces;
using PrismUnityApp.ViewModels;
using PrismUnityApp.Views;
namespace PrismUnityApp
{
class Bootstrapper : UnityBootstrapper
{
#region Overrides of UnityBootstrapper
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType<ISimulationDataFeedView, SimulationDataFeedView>();
Container.RegisterType<ISimulationDataFeedViewModel, SimulationDataFeedViewModel>();
Container.RegisterType<IBarchartDataFeedView, BarchartDataFeedView>();
Container.RegisterType<IBarchartDataFeedViewModel, BarchartDataFeedViewModel>();
Container.RegisterType<IDataFeedManagerView, DataFeedManagerView>();
Container.RegisterType<IDataFeedManagerViewModel, DataFeedManagerViewModel>();
}
protected override DependencyObject CreateShell()
{
return Container.Resolve<ShellView>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
#endregion
}
}
Probably you need to set the region manager manually, in the popup view's code behind (constructor), like this:
RegionManager.SetRegionName( theNameOfTheContentControlInsideThePopup, WellKnownRegionNames.DataFeedRegion );
RegionManager.SetRegionManager( theNameOfTheContentControlInsideThePopup, theRegionManagerInstanceFromUnity );
You'll have to assign a name to the content control hosting the region and somehow acquire the region manager (ServiceLocator.Current.GetInstance<IRegionManager>()).
Related
I'm not sure if this is possible but I'm looking for a way to bind a button to a generic class that contain all the properties i will need to use. Every button needs a relay command so that would be included but all of our buttons will need to bind visibility and being enabled. Instead of having this group of properties and relay command for every button we will use within the given windows view model I was wondering if there was a way to have the button bind to a class then in our view model we reference a new instance of that class for each button needed and then be just be able to set the properties on that class to the values we need. I hope this makes sense.
There's probably a bunch of different ways to do something like this. I don't know if I'd choose to have a class instance for each button. But here's a rough/quick/dodgy example of a solution.
The main model for the form is providing the button models by way of a list. The individual button models then handle the button bindings.
EDIT: Extended the code a bit. Now includes command bindings. Also shows use of ItemsControl as suggested by #Xavier. Hope it helps.
MainWindow.xaml:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<StackPanel>
<!-- Known buttons -->
<StackPanel Margin="20">
<Button DataContext="{Binding ButtonModels[0], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
<Button DataContext="{Binding ButtonModels[1], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
<Button DataContext="{Binding ButtonModels[2], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
</StackPanel>
<!-- Dynamic buttons -->
<StackPanel Margin="20">
<ItemsControl ItemsSource="{Binding ButtonModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Model();
}
}
public class Model
{
private Random rnd = new Random();
public List<ButtonModel> ButtonModels { get; private set; }
public Model()
{
this.ButtonModels = new List<ButtonModel>();
for (int i = 0; i < 5; i++)
{
this.ButtonModels.Add(new ButtonModel
{
LabelText = "Button " + (i + 1),
Command = new RelayCommand((index) => { this.ChangeColour((int)index); }),
CommandParameter = i
});
}
}
private void ChangeColour(int index)
{
this.ButtonModels[index].Colour = new SolidColorBrush(Color.FromRgb((byte)rnd.Next(50, 256), (byte)rnd.Next(50, 256), (byte)rnd.Next(50, 256)));
}
}
public class ButtonModel : ObservableObject
{
private string _LabelText;
public string LabelText { get => _LabelText; set => this.SetProperty(ref _LabelText, value); }
private Brush _Colour = new SolidColorBrush(Color.FromRgb(205, 205, 205));
public Brush Colour { get => _Colour; set => this.SetProperty(ref _Colour, value); }
private RelayCommand _Command;
public RelayCommand Command { get => _Command; set => this.SetProperty(ref _Command, value); }
private int _CommandParameter;
public int CommandParameter { get => _CommandParameter; set => this.SetProperty(ref _CommandParameter, value); }
}
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (field == null && value == null)
{
return false;
}
if (field == null || !field.Equals(value))
{
field = value;
this.RaisePropertyChangedEvent(propertyName);
return true;
}
return false;
}
protected void RaisePropertyChangedEvent(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class RelayCommand : ICommand
{
private Action<object> execute;
private Predicate<object> canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<object> action, Predicate<object> canExecute = null)
{
this.execute = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
}
To simplify, Criticized for writing a novel w/no code a month ago, I made a quick wpf project (uses MVVM) with 2 buttons on the UI.
When a button is clicked, I need my ViewModel to know which one, to route the Speech Synthesizer to the correct Text to Speak. Thanks 4 any help!!
Simple UI Image
<Window x:Class="Wpf_School_Announce.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:Wpf_School_Announce"
xmlns:vm="clr-namespace:Wpf_School_Announce.ViewModels"
mc:Ignorable="d"
Title="Announcements" Height="236.436" Width="293.218">
<Window.Resources>
<vm:ViewModelBase x:Key="viewModel"/>
</Window.Resources>
<Grid DataContext="{Binding Source=viewModel}">
<StackPanel Margin="0,10">
<Button x:Name="btn1stBell" Content="1st Bell" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Margin="0,10"
Command="{Binding ParameterCommand, Source={StaticResource viewModel}}"
CommandParameter="{Binding Command, ElementName=btn1stBell}"/>
<Button x:Name="btnLunchMenu" Content="Lunch Menu" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Margin="0,10"
Command="{Binding ParameterCommand, Source={StaticResource viewModel}}"
CommandParameter="{Binding Command, ElementName=LunchMenu}"/>
</StackPanel>
</Grid>
</Window>
namespace Wpf_School_Announce.ViewModels
{
public class ViewModelBase
{
public ParameterCommand ParameterCommand { get; set; }
public ViewModelBase()
{
ParameterCommand = new ParameterCommand(this);
}
public void ParameterMethod(string <Not sure what needs to go here>)
{
Debug.WriteLine("Parameter Comand:{0}", AnnoucementModel);
//Todo: Need to find out which UI button was clicked to direct The Speech Synthesozer to the correct Speech Text.
}
}
}
namespace Wpf_School_Announce.ViewModels.Commands
{
public class ParameterCommand : ICommand
{
public ViewModelBase ViewModel { get; set; }
public ParameterCommand(ViewModelBase viewModel)
{
ViewModel = viewModel;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
ViewModel.ParameterMethod(parameter as String);
}
}
}
It is a bad solution to just have one command on your viewmodel and to bind every button to it. If you have different things to be executed, define different commands. For that you either have to define a separate class with a dedicated Execute metod for each command or you can use something like RelayCommand of MvvmLight, where you can pass delegates upon creation of each command like this
public class ViewModelBase
{
public RelayCommand BellCommand...
public RelayCommand LunchCommand...
public ViewModelBase()
{
this.BellCommand = new RelayCommand(this.ExecuteBell);
this.LunchCommand = new RelayCommand(this.ExecuteLunch);
}
private void ExecuteBell(object Parameter) {...}
private void ExecuteLunch(object Parameter) {...}
}
and in your XAML
<Button Command="{Binding Path=BellCommand}"... />
<Button Command="{Binding Path=LunchCommand}" ... />
This way you have separate places for the individual logic and your viewmodel must not know anything about your ui - which is good.
Hope it helps.
XAML:
<Button CommandParameter="command_name" Command="{Binding OnClick}" Content="Click Me"></Button>
Event.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
private ICommand onClick;
public ICommand OnClick
{
get
{
return onClick ?? (onClick = new RelayCommand(clickSwitch));
}
}
Class.cs:
private async void clickSwitch(System.Object obj)
{
switch (obj.ToString())
{
case "command_name":
//code
break;
}
I'l start by letting a picture do some talking.
So you see, I want to create a WPF user control that supports binding to a parent window's DataContext. The user control is simply a Button and a ListBox with a custom ItemTemplate to present things with a Label and a Remove Button.
The Add button should call an ICommand on the main view model to interact with the user in selecting a new thing (instance of IThing). The Remove buttons in the ListBoxItem in the user control should similarly call an ICommand on the main view model to request the related thing's removal. For that to work, the Remove button would have to send some identifying information to the view model about the thing requesting to be removed. So there are 2 types of Command that should be bindable to this control. Something like AddThingCommand() and RemoveThingCommand(IThing thing).
I got the functionality working using Click events, but that feels hacky, producing a bunch of code behind the XAML, and rubs against the rest of the pristine MVVM implementation. I really want to use Commands and MVVM normally.
There's enough code involved to get a basic demo working, I am holding off on posting the whole thing to reduce confusion. What is working that makes me feel like I'm so close is the DataTemplate for the ListBox binds the Label correctly, and when the parent window adds items to the collection, they show up.
<Label Content="{Binding Path=DisplayName}" />
While that displays the IThing correctly, the Remove button right next to it does nothing when I click it.
<Button Command="{Binding Path=RemoveItemCommand, RelativeSource={RelativeSource AncestorType={x:Type userControlCommands:ItemManager }}}">
This isn't terribly unexpected since the specific item isn't provided, but the Add button doesn't have to specify anything, and it also fails to call the command.
<Button Command="{Binding Path=AddItemCommand, RelativeSource={RelativeSource AncestorType={x:Type userControlCommands:ItemManager }}}">
So what I need is the "basic" fix for the Add button, so that it calls the parent window's command to add a thing, and the more complex fix for the Remove button, so that it also calls the parent command but also passes along its bound thing.
Many thanks for any insights,
This is trivial, and made so by treating your UserControl like what it is--a control (that just happens to be made up from other controls). What does that mean? It means you should place DependencyProperties on your UC to which your ViewModel can bind, like any other control. Buttons expose a Command property, TextBoxes expose a Text property, etc. You need to expose, on the surface of your UserControl, everything you need for it to do its job.
Let's take a trivial (thrown together in under two minutes) example. I'll leave out the ICommand implementation.
First, our Window
<Window x:Class="UCsAndICommands.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:t="clr-namespace:UCsAndICommands"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<t:ViewModel />
</Window.DataContext>
<t:ItemsEditor Items="{Binding Items}"
AddItem="{Binding AddItem}"
RemoveItem="{Binding RemoveItem}" />
</Window>
Notice we have our Items editor, which exposes properties for everything it needs--the list of items it is editing, a command to add a new item, and a command to remove an item.
Next, the UserControl
<UserControl x:Class="UCsAndICommands.ItemsEditor"
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:t="clr-namespace:UCsAndICommands"
x:Name="root">
<UserControl.Resources>
<DataTemplate DataType="{x:Type t:Item}">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveItem, ElementName=root}"
CommandParameter="{Binding}">Remove</Button>
<TextBox Text="{Binding Name}" Width="100"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Button Command="{Binding AddItem, ElementName=root}">Add</Button>
<ItemsControl ItemsSource="{Binding Items, ElementName=root}" />
</StackPanel>
</UserControl>
We bind our controls to the DPs defined on the surface of the UC. Please, don't do any nonsense like DataContext=this; as this anti-pattern breaks more complex UC implementations.
Here's the definitions of these properties on the UC
public partial class ItemsEditor : UserControl
{
#region Items
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(IEnumerable<Item>),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public IEnumerable<Item> Items
{
get { return (IEnumerable<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
#endregion
#region AddItem
public static readonly DependencyProperty AddItemProperty =
DependencyProperty.Register(
"AddItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand AddItem
{
get { return (ICommand)GetValue(AddItemProperty); }
set { SetValue(AddItemProperty, value); }
}
#endregion
#region RemoveItem
public static readonly DependencyProperty RemoveItemProperty =
DependencyProperty.Register(
"RemoveItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand RemoveItem
{
get { return (ICommand)GetValue(RemoveItemProperty); }
set { SetValue(RemoveItemProperty, value); }
}
#endregion
public ItemsEditor()
{
InitializeComponent();
}
}
Just DPs on the surface of the UC. No biggie. And our ViewModel is similarly simple
public class ViewModel
{
public ObservableCollection<Item> Items { get; private set; }
public ICommand AddItem { get; private set; }
public ICommand RemoveItem { get; private set; }
public ViewModel()
{
Items = new ObservableCollection<Item>();
AddItem = new DelegatedCommand<object>(
o => true, o => Items.Add(new Item()));
RemoveItem = new DelegatedCommand<Item>(
i => true, i => Items.Remove(i));
}
}
You are editing three different collections, so you may want to expose more ICommands to make it clear which you are adding/removing. Or you could cheap out and use the CommandParameter to figure it out.
Refer the below code.
UserControl.XAML
<Grid>
<ListBox ItemsSource="{Binding Things}" x:Name="lst">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ThingName}" Margin="3"/>
<Button Content="Remove" Margin="3" Command="{Binding ElementName=lst, Path=DataContext.RemoveCommand}" CommandParameter="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Window.Xaml
<Window x:Class="MultiBind_Learning.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiBind_Learning"
Title="Window1" Height="300" Width="300">
<StackPanel Orientation="Horizontal">
<Button Content="Add" Width="50" Height="25" Command="{Binding AddCommnd }"/>
<local:UserControl2/>
</StackPanel>
Window.xaml.cs
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = new ThingViewModel();
}
}
ThingViewModel.cs
class ThingViewModel
{
private ObservableCollection<Thing> things = new ObservableCollection<Thing>();
public ObservableCollection<Thing> Things
{
get { return things; }
set { things = value; }
}
public ICommand AddCommnd { get; set; }
public ICommand RemoveCommand { get; set; }
public ThingViewModel()
{
for (int i = 0; i < 10; i++)
{
things.Add(new Thing() { ThingName="Thing" +i});
}
AddCommnd = new BaseCommand(Add);
RemoveCommand = new BaseCommand(Remove);
}
void Add(object obj)
{
things.Add(new Thing() {ThingName="Added New" });
}
void Remove(object obj)
{
things.Remove((Thing)obj);
}
}
Thing.cs
class Thing :INotifyPropertyChanged
{
private string thingName;
public string ThingName
{
get { return thingName; }
set { thingName = value; OnPropertyChanged("ThingName"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
BaseCommand.cs
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged;
public BaseCommand(Action<object> method)
{
_method = method;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}
Instead of Base command you can try RelayCommand from MVVMLight or DelegateCommand from PRISM libraries.
By default, your user control will inherit the DataContext of its container. So the ViewModel class that your window uses can be bound to directly by the user control, using the Binding notation in XAML. There's no need to specify DependentProperties or RoutedEvents, just bind to the command properties as normal.
I have a problem with my XAML. I have a Menu Component, and I would like that it would work in Shortcut key too. I have XAML code, which doesn't work:
<MenuItem Header="_New" Name="New" Click="New_Click" InputGestureText="Ctrl+N">
<MenuItem.InputBindings>
<KeyBinding Key="N" Modifiers="control"/>
</MenuItem.InputBindings>
</MenuItem>
What is the solution? New_Click event works, but Shortcut key doesn't...
Using InputGestureText is only going to add text to the menu item per the documentation. You need to specify what needs to happen when the shortcut is actually performed. To do that you need to create an ICommand in your ViewModel, preferably, then bind that command to the MenuItem.Command
So your resulting code should look like this:
<MenuItem Header="_New" Name="New" Command="{Binding NewCommand}" InputGestureText="Ctrl+N">
assuming you have a public ICommand NewCommand {...} in your view model.
EDIT
Doing this requires a command because that's how WPF works. WPF != WinForms, where in WinForms you would use events and in WPF you want to try to use ICommand bindings. This is proven, and answers your question on why Command is required: it is because InputBinding implements the Command design pattern, so you're not going to really get a way to work around it.
So there isn't really a way to work around using the Click event handler instead of a Command with input gestures. If you're not in the position to use a Command as they are intended to be use (like in MVVM), then you will have to add an ICommand in code-behind, then programmatically set up the binding.
private RelayCommand qatRemoveItemCommand;
public ICommand RemoveItemCommand
{
get
{
if (this.RemoveItemCommand == null)
{
this.RemoveItemCommand = new RelayCommand(param => this.RemoveItem(), param => CanRemoveItem);
}
return this.RemoveItemCommand;
}
}
private void RemoveItem()
{
this.DeleteItem();
}
private bool CanRemoveItem
{
get
{
return true;
}
}
KeyBinding RemoveItemCmdKeyBinding = new KeyBinding(
this.RemoveItemCommand,
Key.N,
ModifierKeys.Control);
New.InputBindings.Add(OpenCmdKeyBinding);
<MenuItem Header="_New" Name="New" InputGestureText="Ctrl+N">
Note it may be required that you Remove or clear the InputBindings when the control is unloaded, but I think this will be as close as you can get, not to mention my original answer answer's your question; your request for additional information is a separate question in itself.
Also, do some research on the classes that implement inherit from InputBinding, KeyBinding and MouseBinding
It is possible to apply shortcut on click.We need to use command binding to achieve this functionality through XAML.
First of all,you need to bind command to the MenuItem and then bind the same command to keybinding.
Following is the working code for that:
<MenuItem Header="_New" Name="New" Command="{Binding NewCommand, Mode=TwoWay}" InputGestureText="Ctrl+N">
<MenuItem.InputBindings>
<KeyBinding Key="N" Modifiers="control" Command="{Binding NewCommand, Mode=TwoWay}"/>
</MenuItem.InputBindings>
</MenuItem>
NewCommand will be of Icommand type in your ViewModel.
I am providing C# code for your understanding of command binding:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyData();
}
}
public class MyData
{
public MyData()
{
this.NewCommand = new DelegateCommand(ExecuteShowMessage);
}
private void ExecuteShowMessage(object obj)
{
MessageBox.Show("Test");
}
public ICommand NewCommand { get; set; }
}
Above code will work as per your requirement.Please Let me know if you have any queries regarding this.
Thank You
Here's my utility class for that:
using System;
using System.Windows;
using System.Windows.Input;
namespace NNN
{
/// <summary>This utility class translates ICommand calls to RoutedEventHandler calls</summary>
class c2e : ICommand
{
readonly RoutedEventHandler eh;
public c2e( RoutedEventHandler eh )
{
this.eh = eh;
}
public event EventHandler CanExecuteChanged;
bool ICommand.CanExecute( object parameter )
{
return true;
}
void ICommand.Execute( object parameter )
{
var a = new RoutedEventArgs();
this.eh( this, a );
}
}
static class Hotkey
{
/// <summary>Register event handler for hotkey</summary>
public static void registerHotkey( this Window wnd, Key key, ModifierKeys modifier, RoutedEventHandler handler )
{
ICommand cmd = new c2e( handler );
InputBinding ib = new InputBinding( cmd,new KeyGesture( key, modifier ) );
wnd.InputBindings.Add( ib );
}
}
}
Usage example:
public MainWindow()
{
InitializeComponent();
this.registerHotkey( Key.O, ModifierKeys.Control, menuOpen );
}
As non of the above solutions worked for me, I would like to suggest another solution here, by using RoutedCommand.
XAML
<Window x:Class="CH02.ContextMenuDemo.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:CH02.ContextMenuDemo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.CommandBindings>
<CommandBinding Command="{x:Static local:MainWindow.MenuItemClickCommand}"
CanExecute="CanExecute"
Executed="OnMenuItemClicked"/>
</Window.CommandBindings>
<Grid>
<TextBlock Text="Right Click here to open Context Menu!"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="My Menu Item"
Name="MyMenuItem"
Command="{x:Static local:MainWindow.MenuItemClickCommand}">
</MenuItem>
<Separator />
<MenuItem Header="Another Menu Item"
IsCheckable="True"
IsChecked="True"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</Grid>
</Window>
CS
public partial class MainWindow : Window
{
private static ICommand _clickCommand;
public static ICommand
MenuItemClickCommand => _clickCommand ??
(_clickCommand = new RoutedUICommand(
text: "Options",
name: "MenuItemClickCommand",
ownerType: typeof(MainWindow),
inputGestures: new InputGestureCollection(
inputGestures: new InputGesture[] {
new KeyGesture(Key.N, ModifierKeys.Control)
})));
public MainWindow()
{
InitializeComponent();
Focus();
}
private void OnMenuItemClicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("Context menu item clicked!");
}
private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true; // or other logic
}
}
I have a TabControl binding to some items. Underneath it is a Button where I can add items dynamically. On adding an item, the new item should become the active Tab (works fine with TabControl.SelectedItem):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TabControl ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=OneWay}">
<TabControl.ContentTemplate>
<DataTemplate>
<this:UserControl1 />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<Button Content="Foo" Click="Button_Click"/>
</StackPanel>
</Window>
Code-behind:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : INotifyPropertyChanged
{
public ObservableCollection<Foo> Items { get; set; }
public Foo SelectedItem { get { return Items.Last(); } }
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
Items = new ObservableCollection<Foo>();
Items.Add(new Foo {Bar = "bar"});
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Items.Add(new Foo {Bar = "bar"});
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Items"));
PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
}
}
}
public class Foo { public string Bar { get; set; } }
}
The UserControl1 looks like this:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox/>
<TextBox x:Name="_textBox"
DataContextChanged="OnDataContextChanged"
Text="{Binding Bar}" />
</StackPanel>
</UserControl>
And the code-behind of it should focus _textBox and selectAll its text when the user clicks on the tab:
using System.Windows;
namespace WpfApplication1
{
public partial class UserControl1
{
public UserControl1()
{
InitializeComponent();
}
private void OnDataContextChanged(object sender,
DependencyPropertyChangedEventArgs e)
{
_textBox.Focus();
_textBox.SelectAll();
}
}
}
I try to achieve that with the DataContextChanged-event, but due to its unpredictability (s.f. http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.datacontextchanged.aspx), it doesn't work all the time. I also tried it with the Loaded-event, but this will be called only once when the DataTemplate is loaded.
So, I think I need to receive the Loaded-event every time the DataContext has changed and the data-binding engine has finished its job. Is there such an event?
Are you wanting to select the text when the user adds a tab AND when the user clicks on a different tab?
If this is the case you may want to to handle this with two event handlers - The tab changed event for the tab control - and then setting it in code when you add a new item.
The DataContext according to your code does not change. It is set to the main window and then inherited down to the child controls.
public MainWindow()
{
Items = new ObservableCollection<Foo>();
Items.Add(new Foo {Bar = "bar"});
InitializeComponent();
DataContext = this;
}