Hi I am having trouble with WPF creating a RoutedUICommand that works similar to an ApplicationCommand. Ideally I want to have a single 'Duplicate' MenuItem that works with two different UserControls. Whether or not the MenuItem is active will depend on which UserControl is selected and if the UserControl contains a selected object (the same way cut, copy and past work on different textboxes, etc).
As per this StackOverflow, question I have created the static class below:
namespace App.Classes
{
public static class myCommands
{
private static readonly RoutedUICommand _duplicateCmd = new RoutedUICommand("Duplicate", "Duplicate", typeof(myCommands));
public static RoutedUICommand Duplicate
{
get { return _duplicateCmd; }
}
}
}
In the main Window's XAML file I have added a reference to the class's namespace and a KeyBinding to the command.
<Window x:Class="GUI.App.Views.Windows.wndMain"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:classes="clr-namespace:App.Classes">
<Window.InputBindings>
<KeyBinding Key="D" Modifiers="Control" Command="{Binding classes:myCommands.Duplicate}" />
</Window.InputBindings>
In the MenuItem I have added a Command binding to the 'Duplicate' command
<MenuItem Name="mnCopy" Header="Copy" Command="ApplicationCommands.Copy" InputGestureText="Ctrl+C" />
<MenuItem Name="mnDuplicate" Header="Duplicate" Command="{Binding classes:myCommands.Duplicate}" InputGestureText="Ctrl+D"/>
And finally in each of my UserControls I have added CommandBindings to the 'Duplicate' and other ApplicationCommands.
<UserControl.CommandBindings>
<CommandBinding Command="ApplicationCommands.Copy" Executed="CommandBinding_Copy" CanExecute="CommandBindingCanExecute_Copy" />
<CommandBinding Command="classes:myCommands.Duplicate" Executed="CommandBinding_DuplicateControls" CanExecute="CommandBindingCanExecute_DuplicateControls" />
</UserControl.CommandBindings>
The code-behind for my Executed and CanExecute for each of the UserControls is
/// <summary>
///
/// </summary>
public void CommandBinding_Copy(Object sender, ExecutedRoutedEventArgs e)
{
_viewModel.Copy();
}
/// <summary>
///
/// </summary>
public void CommandBinding_Duplicate(Object sender, ExecutedRoutedEventArgs e)
{
_viewModel.Duplicate();
}
/// <summary>
///
/// </summary>
public void CommandBindingCanExecute_Copy(Object sender, CanExecuteRoutedEventArgs e)
{
_viewModel.CanExecute_Copy();
}
/// <summary>
///
/// </summary>
public void CommandBindingCanExecute_Duplicate(Object sender, CanExecuteRoutedEventArgs e)
{
_viewModel.CanExecute_Duplicate();
}
This works perfectly for my ApplicationCommands and the appropriate Executed and CanExecute get called depending upon which UserControl is selected. But I can't get it to work with my 'Duplicate' RoutedUICommand. Does anybody know what I'm doing wrong or am missing? Will be grateful for any help anyone can give.
You're binding to a static property via Path and that won't work. Change Binding to x:Static and should work
<MenuItem Name="mnCopy" Command="ApplicationCommands.Copy" />
<MenuItem Name="mnDuplicate" Command="{x:Static classes:myCommands.Duplicate}"/>
On a side note RoutedUICommand has overloaded constructor that accepts InputGestureCollection where you can bind Ctrl+D when you command is created.
private static readonly RoutedUICommand _duplicateCmd =
new RoutedUICommand(
"Duplicate",
"Duplicate",
typeof(myCommands),
new InputGestureCollection(new InputGesture[] { new KeyGesture(Key.D, ModifierKeys.Control) }));
advantage is that you won't have to specify InputGestureText, as you don't have to ApplicationCommands.Copy, and because you use RoutedUICommand you also don't need to specify Header. If you do that both InputGestureText and Header will be taken by default from your RoutedUICommand and all you actually need to specify against MenuItem is a Command
Related
I'm trying to execute an ICommand (AsyncRelayCommand) on the loaded event of my UserControl using the Microsoft.Xaml.Behaviors.Wpf library.
<UserControl x:Class="..."
...
xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"
DataContext="{Binding ViewModel, Source={StaticResource ViewModelLocator}}">
<Behaviors:Interaction.Triggers>
<Behaviors:EventTrigger EventName="Loaded">
<Behaviors:InvokeCommandAction Command="{Binding LoadCommand}" />
</Behaviors:EventTrigger>
</Behaviors:Interaction.Triggers>
...
</UserControl>
The command property is called correctly, the Command class is created and it also calls correctly the CanExecute method (which returns true).
public override ICommand LoadCommand
{
get { return new AsyncRelayCommand(
async () => { /*never executed*/ },
() => return !this.IsLoading); // canExecute logic executed (true)
}
}
But it never calls the Execute method.
I believe I need somehow tell that I want the UserControl's event, but all examples are looking the same as mine.
I believe the design as described, does not adhere to separation of concerns between the user control and an external viewmodel.
I would instead use the Loaded event of the UserControl and create a specific dependency property on the control which the user will have bound the ICommand to when wiring up the control.
Example
#region public ICommand LoadCommand
/// <summary></summary>
public ICommand LoadCommand
{
get => GetValue(LoadCommandProperty) as ICommand;
set => SetValue(LoadCommandProperty, value);
}
/// <summary>
/// Identifies the LoadCommand dependency property.
/// </summary>
public static readonly DependencyProperty LoadCommandProperty =
DependencyProperty.Register(
"LoadCommand",
typeof(ICommand),
typeof(MainUserControl),
new PropertyMetadata(null));
#endregion public ICommand LoadCommand
private void MainUserControl_OnLoaded(object sender, RoutedEventArgs e)
=> LoadCommand?.Execute(null);
Very high up the visual tree in WPF's Expander control is a border element (see screenshot). By default this has a CornerRadius of 3. Is it possible to modify this value?
I'll leave marking as answer for now but I managed to implement the solution as follows:
Using stylesnooper I obtained the style / control template used for the 'standard' Expander control.
Then after discovering it didn't quite behave as expected, figured out that the line <ToggleButton IsChecked="False" ... is wrong and should actually be <ToggleButton IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"...
Everything then worked as expected.
I made a behavior which modifies the first found border in the ControlTemplate. You can easily extend the behavior with new properties where u want to modify
/// <summary>
/// modifies the first found <see cref="Border"/> in the <see cref="ControlTemplate"/> of the attached <see cref="Control"/>
/// </summary>
public class ModifyBorderBehavior : Behavior<Control>
{
// ##############################################################################################################################
// Properties
// ##############################################################################################################################
#region Properties
/// <summary>
/// The new corner radius
/// </summary>
public CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
/// <summary>
/// The <see cref="CornerRadius"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(ModifyBorderBehavior));
#endregion
// ##############################################################################################################################
// Constructor
// ##############################################################################################################################
#region Constructor
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += _OnLoaded;
}
private void _OnLoaded(object sender, RoutedEventArgs e)
{
//var children = VisualTree.GetVisualChildCollection<Border>(sender);
if (sender is Control control)
{
Border border = VisualTree.GetVisualChild<Border>(control);
if(ReadLocalValue(CornerRadiusProperty) != DependencyProperty.UnsetValue)
border.CornerRadius = CornerRadius;
}
}
#endregion
}
<Expander>
<i:Interaction.Behaviors>
<zls:ModifyBorderBehavior CornerRadius="0"/>
</i:Interaction.Behaviors>
</Expander>
Here is my issue, I created a UserControl as follows:
XAML:
<UserControl x:Class="ProcessVisualizationBar.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-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" xmlns:lb="clr-namespace:ProcessVisualizationBar"
Name="ProcessVisualizationBar">
<Border BorderBrush="Silver" BorderThickness="1,1,1,1" Margin="0,5,5,5" CornerRadius="5" Padding="2">
<ListBox Name="ProcessVisualizationRibbon" Grid.Column="1" Height="40" ItemsSource="{Binding ElementName=ProcessVisualizationBar, Path=ItemsSource}"/>
</Border>
</UserControl>
Code Behind(C#):
using System.Windows;
using System.Windows.Controls;
namespace ProcessVisualizationBar
{
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(System.Collections.IEnumerable), typeof(UserControl));
public System.Collections.IEnumerable ItemsSource
{
get { return ProcessVisualizationRibbon.ItemsSource; }
set { ProcessVisualizationRibbon.ItemsSource = value; }
}
public UserControl1()
{
InitializeComponent();
}
}
}
I build my Usercontrol and add the .dll to the reference of another project. I add the reference at the top of my XAML as such:
xmlns:uc="clr-namespace:ProcessVisualizationBar;assembly=ProcessVisualizationBar"
Then I go to use the control.
<uc:UserControl1 Grid.Row="2" x:Name="ProcessVisualizationContent" />
It finds the control okay, but when I try and find the ItemsSource Property I added to it, I'm not finding it. I'm not sure what I missed, and I'm not sure what debug tools are really available to figure this out.
Anyone have some experience with this that can share their wisdom?
What is the actual data being passed? That is what you should be creating and not a pass through situation which you are attempting.
Create a dependency property targetting the actual data to be passed with a property changed handler. On the change event, then call internal code to bind it to the ProcessVisualazation ItemsSource. That way you can debug when the data comes through by placing a breakpoint in the event.
Here is an example where the consumer will see StringData in the Xaml and needs to pas a list of strings into the custom control:
#region public List<string> StringData
/// <summary>
/// This data is to be bound to the ribbon control
/// </summary>
public List<string> StringData
{
get { return GetValue( StringDataProperty ) as List<string>; }
set { SetValue( StringDataProperty, value ); }
}
/// <summary>
/// Identifies the StringData dependency property.
/// </summary>
public static readonly System.Windows.DependencyProperty StringDataProperty =
System.Windows.DependencyProperty.Register(
"StringData",
typeof( List<string> ),
typeof( UserControl ),
new System.Windows.PropertyMetadata( null, OnStringDataPropertyChanged ) );
/// <summary>
/// StringDataProperty property changed handler.
/// </summary>
/// <param name="d">DASTreeBinder that changed its StringData.</param>
/// <param name="e">Event arguments.</param>
private static void OnStringDataPropertyChanged( System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e )
{
UserControl source = d as UserControl;
List<string> value = e.NewValue as List<string>;
BindDataToRibbon( value );
}
#endregion public List<string> StringData
Now just create a BindDataToRibbon method which will do the dirty work. Note that I use Jeff Wilcox's Silverlight dependency snippets in Visual Studio to generate the above dependency. I have used it for WPF and Silverlight projects.
I am new to wpf application and I am working on application and i have created a menu Now i want to function menu items event on short key ctrl+o, ctrl+n etc. How can i do it.please give me in details.
You can so it in the following way....
In Xaml file
<Window x:Class="FocusDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FocusDemo"
Title="Window1" Height="300" Width="300">
<Window.CommandBindings>
<CommandBinding
Command=
"{x:Static
local:Window1.CustomRoutedCommand}"
Executed="ExecutedCustomCommand"
CanExecute="CanExecuteCustomCommand" >
</CommandBinding>
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding
Command=
"{x:Static
local:Window1.CustomRoutedCommand}"
Key="S"
Modifiers="Control"/>
</Window.InputBindings>
<Grid>
<!--Your Controls-->
</Grid>
</Window>
In the Code behind file
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public static RoutedCommand CustomRoutedCommand = new RoutedCommand();
public Window1()
{
InitializeComponent();
}
#region
public void ExecutedCustomCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Ctrl+S");
}
public void CanExecuteCustomCommand(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
}
Source : Click here
Please dont forget to mark the answer if its correct
I know it is not exact answer to the question, but probably anybody like me was searching for the way to put ANY keyboard shortcuts to menu items (command buttons) like Alt+O, Alt+N. In this case, you can just put undescore character (_) before the shortcut character in the item name.
Is there any way to detect a change in the Text property of a TextBlock element using events?
(I'm trying to provide an animation for highlighting the TextBlocks whose Text property change within a DataGrid)
It's easier than that! Late answer, but much simpler.
// assume textBlock is your TextBlock
var dp = DependencyPropertyDescriptor.FromProperty(
TextBlock.TextProperty,
typeof(TextBlock));
dp.AddValueChanged(textBlock, (sender, args) =>
{
MessageBox.Show("text changed");
});
This is like the code from the link in bioskope's answer, but simplified. You need the TargetUpdated event and add NotifyOnTargetUpdated=True to the binding.
<TextBlock Text="{Binding YourTextProperty, NotifyOnTargetUpdated=True}"
TargetUpdated="YourTextEventHandler"/>
As far as I can understand there isn't any textchanged event in TextBlock. Looking at your requirement, I feel that re-templating a textbox will also not be a viable solution. From my preliminary searching around, this seems to be a possible solution.
<TextBlock x:Name="tbMessage" Text="{Binding Path=StatusBarText, NotifyOnTargetUpdated=True}">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:0″
To="1.0″ />
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:2″
From="1.0″ To="0.0″ BeginTime="0:0:5″ />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
Bind the Text property to a DependencyProperty, which has an event trigger:
public static string GetTextBoxText(DependencyObject obj)
{
return (string)obj.GetValue(TextBoxTextProperty);
}
public static void SetTextBoxText(DependencyObject obj, string value)
{
obj.SetValue(TextBoxTextProperty, value);
}
public static readonly DependencyProperty TextBoxTextProperty =
DependencyProperty.RegisterAttached(
"TextBoxText",
typeof(string),
typeof(TextBlockToolTipBehavior),
new FrameworkPropertyMetadata(string.Empty, TextBoxTextChangedCallback)
);
private static void TextBoxTextChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBlock textBlock = d as TextBlock;
HandleTextChange(textBlock);
}
In the XAML Bind to the TextBlock text Property:
<TextBlock
Text="{Binding SomeProperty, UpdateSourceTrigger=PropertyChanged}"
th:TextBlockBehavior.TextBoxText="{Binding Text,
RelativeSource={RelativeSource Self}}" />
Here is a similar example on MSDN using code-behind:
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.targetupdated.aspx
Here's something you can use I picked up from Jerry Nixon and Daren May at the Microsoft Virtual Academy "Developing Universal Windows Apps with C# and XAML" and the code that contains the DependencyObject logic is here "(W8.1-WP8.1) UNIVERSAL APP FOR MVA".
namespace App1.Behaviors
{
// <summary>
/// Helper class that allows you to monitor a property corresponding to a dependency property
/// on some object for changes and have an event raised from
/// the instance of this helper that you can handle.
/// Usage: Construct an instance, passing in the object and the name of the normal .NET property that
/// wraps a DependencyProperty, then subscribe to the PropertyChanged event on this helper instance.
/// Your subscriber will be called whenever the source DependencyProperty changes.
/// </summary>
public class DependencyPropertyChangedHelper : DependencyObject
{
/// <summary>
/// Constructor for the helper.
/// </summary>
/// <param name="source">Source object that exposes the DependencyProperty you wish to monitor.</param>
/// <param name="propertyPath">The name of the property on that object that you want to monitor.</param>
public DependencyPropertyChangedHelper(DependencyObject source, string propertyPath)
{
// Set up a binding that flows changes from the source DependencyProperty through to a DP contained by this helper
Binding binding = new Binding
{
Source = source,
Path = new PropertyPath(propertyPath)
};
BindingOperations.SetBinding(this, HelperProperty, binding);
}
/// <summary>
/// Dependency property that is used to hook property change events when an internal binding causes its value to change.
/// This is only public because the DependencyProperty syntax requires it to be, do not use this property directly in your code.
/// </summary>
public static DependencyProperty HelperProperty =
DependencyProperty.Register("Helper", typeof(object), typeof(DependencyPropertyChangedHelper), new PropertyMetadata(null, OnPropertyChanged));
/// <summary>
/// Wrapper property for a helper DependencyProperty used by this class. Only public because the DependencyProperty syntax requires it.
/// DO NOT use this property directly.
/// </summary>
public object Helper
{
get { return (object)GetValue(HelperProperty); }
set { SetValue(HelperProperty, value); }
}
// When our dependency property gets set by the binding, trigger the property changed event that the user of this helper can subscribe to
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var helper = (DependencyPropertyChangedHelper)d;
helper.PropertyChanged(d, e);
}
/// <summary>
/// This event will be raised whenever the source object property changes, and carries along the before and after values
/// </summary>
public event EventHandler<DependencyPropertyChangedEventArgs> PropertyChanged = delegate { };
}
}
Usage XAML:
<TextBlock Grid.Row="0"
x:Name="WritingMenuTitle"
HorizontalAlignment="Left"
FontSize="32"
FontWeight="SemiBold"
Text="{Binding WritingMenu.Title}"
TextAlignment="Left"
TextWrapping="Wrap"/>
Usage xaml.cs:
Behaviors.DependencyPropertyChangedHelper helper = new Behaviors.DependencyPropertyChangedHelper(this.WritingMenuTitle, Models.CommonNames.TextBlockText);
helper.PropertyChanged += viewModel.OnSenarioTextBlockTextChangedEvent;
Usage viewmodel.cs:
public async void OnSenarioTextBlockTextChangedEvent(object sender, DependencyPropertyChangedEventArgs args)
{
StringBuilder sMsg = new StringBuilder();
try
{
Debug.WriteLine(String.Format(".....WritingMenuTitle : New ({0}), Old ({1})", args.NewValue, args.OldValue));
}
catch (Exception msg)
{
#region Exception
.....
#endregion
}
}