I have a button and a context menu bound to the same Command, when I start the app, the button is enabled, but the context menu item isn't (as I said, they're bound to the same command). The context menu item becomes enabled only after I click the button. Does anybody know why?
This is the behavior.
And this is the XAML:
<Window.CommandBindings>
<CommandBinding Command="local:LocalCommandManager.ShowDialogCommand" CanExecute="CanExecuteShowDialogCommand" Executed="ShowDialogCommandExecuted" />
<CommandBinding Command="local:LocalCommandManager.DontShowDialogCommand" CanExecute="CanExecuteDontShowDialogCommand" Executed="DontShowDialogCommandExecuted" />
</Window.CommandBindings>
<Window.ContextMenu>
<ContextMenu>
<MenuItem Command="local:LocalCommandManager.ShowDialogCommand" />
<MenuItem Command="local:LocalCommandManager.DontShowDialogCommand" />
</ContextMenu>
</Window.ContextMenu>
<Grid Background="Red">
<Button Command="local:LocalCommandManager.ShowDialogCommand" Content="Show Dialog" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="6" />
</Grid>
Thanks!
EDIT:
Command code:
public static class LocalCommandManager
{
private static object syncRoot = new object();
private static RoutedUICommand _showDialogCommand;
public static RoutedUICommand ShowDialogCommand
{
get
{
lock (syncRoot)
{
if (_showDialogCommand == null)
_showDialogCommand = new RoutedUICommand("Show Dialog", "ShowDialogCommand", typeof(LocalCommandManager));
return _showDialogCommand;
}
}
}
}
Command event handlers (in MainWindow.xaml.cs):
private void CanExecuteShowDialogCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void ShowDialogCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Dialog");
}
Related
`I am working on a WPF application (MVVM)
I have a user control(uc1) that has four buttons. cancel,accept,exit
I am going to use this control in multiple views.
I need to cancel button to revert the changes what user will make in propertygrig
user control:
<UserControl x:Class="WPF.CustomControl.RadPropertyWindowButtons"
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"
d:DesignHeight="45" d:DesignWidth="700">
<Grid>
<Grid Uid="radpropertybuttons" Height="39" VerticalAlignment="Bottom" Margin="74,0,-108,0">
<Button x:Name="Cancel"
Command="{Binding radpropertyCancel}" >
</Button>
<Button x:Name="Accept"
Command="{Binding radpropertyAccept}">
</Button>
<Button x:Name="Exit"
Command="{Binding radpropertyExit}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
</Button>
</Grid>
</Grid>
</UserControl>
view:
<Grid Height="564" VerticalAlignment="Top" >
<Grid HorizontalAlignment="Center">
<telerik:RadLayoutControl
Name="PropertyGridContainer"
Orientation="Vertical">
</telerik:RadLayoutControl>
</Grid>
<Grid VerticalAlignment="Bottom">
<customcontrol:RadPropertyWindowButtons x:Name="ucPropertyButtons" Height="44" VerticalAlignment="Top" HorizontalAlignment="Center" Loaded="RadPropertyWindowButtons_Loaded" />
</Grid>
</Grid>
in view model
public ICommand radpropertyCancel { get; set; }
radpropertyCancel = new ViewModelCommand(execradpropertyCancel);
private void execradpropertyCancel(object obj)
{
this.RevertToOriginalData();
}
how to clear the PropertyGridContainer and bind with the data that we get from RevertToOriginalData`
I do it like this if i do from code behind if i use click event but how to do it with command.
this._viewModel.RevertToOriginalData();
this.PropertyGridContainer.Items.Clear();
this.PropertyGridContainer.Items.Add(this._viewModel.myGrid);
this.ViewModel.IsDirty = false;
this._viewModel.myGrid is wrong design if myGrid is really a Grid ( a UI element). Your view model classes must never handle UI elements or participate in/implement UI logic.
Data changes are always handled outside the view (where the data lives). Layout on the other hand is always the domain of the view.
If you want to revert the layout changes made by the user, you must do this completely in the view (code-behind).
To accomplish this, a parent control (e.g., Window) that hosts both, the RadPropertyWindowButtons and the RadLayoutControl, should expose the related commands as routed commands.
Then in the command handlers you save (serialize) the layout before edit (or alternatively on accept/after edit) and restore (deserialize) it in case the edit procedure was cancelled. The RadLayoutControl exposes a related API to help with the serialization.
Now, that the implementation of the custom control no longer depends on the explicit view model class type, the RadPropertyWindowButtons has become fully reusable in any context.
In general, to enable reusability of controls they must express their (data) dependencies as dependency properties, that are later bound to the current DataContext. The internals of the reusable control simply bind to these dependency properties (instead of binding to an explicit DataContext type). Otherwise they are only "reusable" with a particular DataContext.
MainWindow.xaml.cs
partial class MainWindow : Window
{
public static RoutedUICommand CancelEditLayoutCommand { get; } = new RoutedUICommand(
"Cancel layout edit and revert to previous state",
nameof(MainWindow.CancelEditLayoutCommand),
typeof(MainWindow));
public static RoutedUICommand AcceptLayoutCommand { get; } = new RoutedUICommand(
"Accept the current layout",
nameof(MainWindow.AcceptLayoutCommand),
typeof(MainWindow));
private Dictionary<RadLayoutControl, bool> IsInEditModeTable { get; }
private string SerializedLayout { get; set; }
public MainWindow()
{
InitializeComponent();
this.IsInEditModeTable = new Dictionary<RadLayoutControl, bool>();
var cancelEditLayoutCommandBinding = new CommandBinding(MainWindow.CancelEditLayoutCommand, ExecuteCancelEditLayoutCommand, CanExecuteCancelEditLayoutCommand);
_ = this.CommandBindings.Add(cancelEditLayoutCommandBinding);
var acceptLayoutCommandBinding = new CommandBinding(MainWindow.AcceptLayoutCommand, ExecuteAcceptLayoutCommand, CanExecuteAcceptLayoutCommand);
_ = this.CommandBindings.Add(acceptLayoutCommandBinding);
}
private void CanExecuteCancelEditLayoutCommand(object sender, CanExecuteRoutedEventArgs e)
=> e.CanExecute = e.Parameter is RadLayoutControl targetControl
&& this.IsInEditModeTable.TryGetValue(targetControl, out bool isTargetControlInEditMode)
&& isTargetControlInEditMode;
private void ExecuteCancelEditLayoutCommand(object sender, ExecutedRoutedEventArgs e)
{
var targetControl = (RadLayoutControl)e.Parameter;
RestoreLayout(targetControl);
this.IsInEditModeTable[targetControl] = false;
}
private void CanExecuteAcceptLayoutCommand(object sender, CanExecuteRoutedEventArgs e)
=> e.CanExecute = e.Parameter is RadLayoutControl targetControl
&& this.IsInEditModeTable.TryGetValue(targetControl, out bool isTargetControlInEditMode)
&& isTargetControlInEditMode;
private void ExecuteAcceptLayoutCommand(object sender, ExecutedRoutedEventArgs e)
{
var targetControl = (RadLayoutControl)e.Parameter;
SaveLayout(targetControl);
this.IsInEditModeTable[targetControl] = false;
}
// Instead of handling the SelectionChanged event I recommend
// to introduce another routed command that allows the user to put the RadLayoutControl into edit mode (by setting the RadLayoutControl.IsInEditMode accordingly).
// Aside from an improved UX this would provide a better flow or trigger to kickoff the serialization
private void OnLayoutControlSelectionChanged(object sender, LayoutControlSelectionChangedEventArgs e)
{
var targetControl = sender as RadLayoutControl;
if (this.IsInEditModeTable.TryGetValue(targetControl, out bool isTargetControlInEditMode)
&& isTargetControlInEditMode)
{
return;
}
isTargetControlInEditMode = e.NewItem is not null;
if (isTargetControlInEditMode)
{
SaveLayout(targetControl);
}
this.IsInEditModeTable[targetControl] = isTargetControlInEditMode;
}
private void SaveLayout(RadLayoutControl targetControl)
=> this.SerializedLayout = targetControl.SaveToXmlString();
private void RestoreLayout(RadLayoutControl targetControl)
=> targetControl.LoadFromXmlString(this.SerializedLayout);
}
MainWindow.xaml
<Window>
<StackPanel>
<telerik:RadLayoutControl Name="PropertyGridContainer"
IsInEditMode="True"
telerik:RadLayoutControl.SerializationId="PropertyGridContainerID"
SelectionChanged="OnLayoutControlSelectionChanged" />
<customcontrol:RadPropertyWindowButtons TargetControl="{Binding ElementName=PropertyGridContainer}" />
</StackPanel>
</Window>
RadPropertyWindowButtons.xaml.cs
class RadPropertyWindowButtons
{
public RadLayoutControl TargetControl
{
get => (RadLayoutControl)GetValue(TargetControlProperty);
set => SetValue(TargetControlProperty, value);
}
public static readonly DependencyProperty TargetControlProperty = DependencyProperty.Register(
"TargetControl",
typeof(RadLayoutControl),
typeof(RadPropertyWindowButtons),
new PropertyMetadata(default));
}
RadPropertyWindowButtons.xaml
<UserControl>
<StackPanel>
<Button x:Name="Cancel"
Command="{x:Static local:MainWindow.CancelEditLayoutCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=TargetControl}" />
<Button x:Name="Accept"
Command="{x:Static local:MainWindow.AcceptLayoutCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=TargetControl}" />
</StackPanel>
</UserControl>
See Save/Load Layout for more advanced scenarios.
Code behind:
public class LoginButton : Button
{
public static DependencyProperty LoginedProperty;
public static DependencyProperty LoginEventProperty;
public delegate void LoginEventDelegate(object sender, RoutedEventArgs e);
static LoginButton()
{
LoginedProperty = DependencyProperty.Register("Logined", typeof(Boolean), typeof(LoginButton));
LoginEventProperty = DependencyProperty.Register("LoginEvent", typeof(LoginEventDelegate), typeof(LoginButton));
}
public Boolean Logined
{
get { return (Boolean)base.GetValue(LoginedProperty); }
set { base.SetValue(LoginedProperty, value); }
}
public event LoginEventDelegate LoginEvent;
protected virtual void OnLoginEvent(object sender, RoutedEventArgs e)
{
if (LoginEvent != null)
LoginEvent(sender,e);
}
}
XAML:
<ControlTemplate x:Key="LoginButtonTemplate" TargetType="local:LoginButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ContentPresenter VerticalAlignment="Center" Margin="10,0,0,0"></ContentPresenter>
<Grid Grid.Column="1" Margin="5,0,0,0">
<Label Name="L" VerticalAlignment="Center" Padding="0" Foreground="#919191" Visibility="Collapsed">logined</Label>
<Button Name="B" Template="{StaticResource CustomButton}" Background="Transparent" BorderThickness="0" Padding="0" Foreground="#3598db" Content="click login"></Button>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Logined" Value="true">
<Setter TargetName="L" Property="Visibility" Value="Visible"></Setter>
<Setter TargetName="B" Property="Visibility" Value="Collapsed"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
--------------------------------
I want to achieve this function:
The button which Name="B" in ControlTemplate,when I click it,it work the custom event OnLoginEvent?
How can i do it?
--------------------------------
Oh,it seems no one knows my meaning for my poor English.
Now I explain the function for more detailed.
The software needs to manage about 100 or more account,if the account is not logined,it will show the button let user to login.And if is logined it will show the label that is logined
So I make a new Custom usercontrol named LoginButton,and create a boolean 'Logined' to control button if is logined.
Beaucase of different account has different login function.So I create a new event 'LoginEvent' to apply different login function.Now the question is the button Name="B" which to login.I need binding the button Name="B" click event or previewmousedown event to the event 'LoginEvent'.But I can't find the way to binding it.
Please help me,thanks a lot.
You could override the OnApplyTemplate method of the LoginButton class and hook up an event handler to the click event for the "B" button in the template that raises the event.
LoginEvent should be an event and not a dependency property though.
Try this:
public class LoginButton : Button
{
public static DependencyProperty LoginedProperty;
public delegate void LoginEventDelegate(object sender, RoutedEventArgs e);
static LoginButton()
{
LoginedProperty = DependencyProperty.Register("Logined", typeof(Boolean), typeof(LoginButton));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Button b = this.Template.FindName("B", this) as Button;
if(b != null)
{
b.Click += (s, e) => OnLoginEvent(s, e);
}
}
public Boolean Logined
{
get { return (Boolean)base.GetValue(LoginedProperty); }
set { base.SetValue(LoginedProperty, value); }
}
public event LoginEventDelegate LoginEvent;
protected virtual void OnLoginEvent(object sender, RoutedEventArgs e)
{
if (LoginEvent != null)
LoginEvent(sender, e);
}
}
Usage:
<local:LoginButton Template="{StaticResource LoginButtonTemplate}" LoginEvent="LoginButton_LoginEvent"/>
private void LoginButton_LoginEvent(object sender, RoutedEventArgs e)
{
MessageBox.Show("Login!");
}
I am developing a desktop software using WPF and I would like to use tab control to view different forms. Am also studying MVVM. I have managed to create a new tab control every time a menu is clicked. How can I set the tabitem header to be the same as the menuitem clicked header. Here is the code of what I have done so far!
Here is the view:
<MenuItem Header="_File">
<MenuItem Command="{Binding NewWorkspaceCommand}" Header="_New Tab" InputGestureText="Ctrl + N" />
<MenuItem Command="{Binding CloseWorkspaceCommand}" Header="_Close Tab" InputGestureText="Ctrl + F4" />
<MenuItem Command="{Binding ExitCommand}" Header="E_xit" InputGestureText="Ctrl + X" />
</MenuItem>
<TabControl x:Name="tbMain" ItemsSource="{Binding Workspaces}" SelectedIndex="{Binding SelectedIndex}" Opacity=" 0.90" VerticalAlignment="Top" Height="{Binding Path=ActualHeight,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window }}}">
<TabControl.Background >
<ImageBrush ImageSource="/Resources/images/background.png" Stretch="UniformToFill" />
</TabControl.Background>
<TabControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Header}" />
<Button Command="{Binding CloseCommand}" Content="X" Margin="4,0,0,0" FontFamily="Courier New" Width="17" Height="17" VerticalContentAlignment="Center" />
</WrapPanel>
</DataTemplate>
</TabControl >
View Model
public class MainViewModel : ViewModelBase
{
#region Constructor
public MainViewModel()
{
invokemenuClick;
Workspaces = new ObservableCollection<WorkspaceViewModel>();
Workspaces.CollectionChanged += Workspaces_CollectionChanged;
}
#endregion
#region Event Handlers
void Workspaces_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null && e.NewItems.Count != 0)
foreach (WorkspaceViewModel workspace in e.NewItems)
workspace.RequestClose += this.OnWorkspaceRequestClose;
if (e.OldItems != null && e.OldItems.Count != 0)
foreach (WorkspaceViewModel workspace in e.OldItems)
workspace.RequestClose -= this.OnWorkspaceRequestClose;
}
private void OnWorkspaceRequestClose(object sender, EventArgs e)
{
CloseWorkspace();
}
#endregion
#region Commands
private DelegateCommand _exitCommand;
public ICommand ExitCommand
{
get { return _exitCommand ?? (_exitCommand = new DelegateCommand(() => Application.Current.Shutdown())); }
}
private DelegateCommand _newWorkspaceCommand;
public ICommand NewWorkspaceCommand
{
get { return _newWorkspaceCommand ?? (_newWorkspaceCommand = new DelegateCommand(NewWorkspace)); }
}
private void NewWorkspace()
{
frmCustomer oCustomer = new frmCustomer();
var workspace = new WorkspaceViewModel { Header = "New Tab" } ;
Workspaces.Add(workspace);
/// = oCustomer.Content;
SelectedIndex = Workspaces.IndexOf(workspace);
}
private DelegateCommand _closeWorkspaceCommand;
public ICommand CloseWorkspaceCommand
{
get { return _closeWorkspaceCommand ?? (_closeWorkspaceCommand = new DelegateCommand(CloseWorkspace, () => Workspaces.Count > 0)); }
}
private void CloseWorkspace()
{
Workspaces.RemoveAt(SelectedIndex);
SelectedIndex = 0;
}
#endregion
I would like to set the calling menu header where it says var workspace = new WorkspaceViewModel { Header = "New Tab" } ; where "New Tab" is the name of the calling menuItem.
I hope I have explained myself.
Thanx in advance for your help!
`
I want to bind a focus behavior to a reset button that will put the focus on the control named in the ElementToFocus property
<Style TargetType="Button" x:Key="Button_Reset" BasedOn="{StaticResource Button_Default}" >
<Setter Property="ElementToFocus" />
<Setter Property="behaviors:EventFocusAttachment.ElementToFocus" Value="{Binding ElementName=ElementToFocus}" />
</Style>
Control Markup:
<Button
x:Name="button_Clear"
Style="{DynamicResource Button_Reset}"
HorizontalAlignment="Right"
Content="Clear"
Command="{Binding Path=ClearCommand}"
ElementToFocus="textbox_SearchText"
Margin="0,0,0,7" />
How can I accomplish this?
I have created an attached behavior to try and achieve what you are trying to do.
Attached Behavior Code:
public static class ElementFocusBehavior
{
public static readonly DependencyProperty ElementToFocusProperty =
DependencyProperty.RegisterAttached("ElementToFocus", typeof (FrameworkElement), typeof (ElementFocusBehavior), new PropertyMetadata(default(FrameworkElement), PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var button = dependencyObject as Button;
if (button == null) return;
if (button.IsLoaded)
{
AddClickHandler(button);
}
else
{
button.Loaded += ButtonOnLoaded;
}
}
private static void ButtonOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var button = (Button) sender;
button.Loaded -= ButtonOnLoaded;
AddClickHandler(button);
}
static void AddClickHandler(Button button)
{
button.Click += ButtonOnClick;
}
private static void ButtonOnClick(object sender, RoutedEventArgs routedEventArgs)
{
var fe = GetElementToFocus(sender as Button) as FrameworkElement;
if (fe == null) return;
fe.Focus();
}
public static void SetElementToFocus(Button button, FrameworkElement value)
{
button.SetValue(ElementToFocusProperty, value);
}
public static FrameworkElement GetElementToFocus(Button button)
{
return (FrameworkElement) button.GetValue(ElementToFocusProperty);
}
}
And the XAML for Button:
<Button Content="Reset" local:ElementFocusBehavior.ElementToFocus="{Binding ElementName=TextBoxThree, Path=.}" />
Sample code from my MainWindow:
<StackPanel>
<TextBox Name="TextBoxOne" />
<TextBox Name="TextBoxTwo" />
<TextBox Name="TextBoxThree" />
<Button Content="Reset" local:ElementFocusBehavior.ElementToFocus="{Binding ElementName=TextBoxThree, Path=.}" />
</StackPanel>
Basically, what I did was,
have an attached behavior to store the element to be focused,
and then in the attached behavior add event handler to button Click event,
in the Click event set the Focus on the ElementToFocus element
Hope this helps.
How can I retrieve the CommandTarget in the Executed callback of a RoutedCommand?
Thanks.
Edit: adding a verbose sample
Commands class:
static class Commands
{
public static readonly RoutedCommand MyCommand = new RoutedCommand();
}
XAML code
<Window.CommandBindings>
<CommandBinding Command="{x:Static BasicWpfCommanding:Commands.MyCommand}"
CanExecute="MyCommandCanExecute"
Executed="MyCommandExecuted"/>
</Window.CommandBindings>
<StackPanel>
<Button Command="{x:Static BasicWpfCommanding:Commands.MyCommand}"
CommandParameter="#FF303030"
CommandTarget="{Binding ElementName=aButton}"
Name="aButton">A Command</Button>
</StackPanel>
Commands callbacks
private void MyCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
//var target = (Button)sender fires an ecception: in effect "sender" is the main window...
}
var target = e.Source;