What technique could be used in WPF to show TextBlock Text based on the Button that has mouse over on it?
The UI is organized as follows:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel>
<Button Content="Item1" Tag="This is the text for Item1"/>
<Button Content="Item2" Tag="This is the text for Item2"/>
<Button Content="Item3" Tag="This is the text for Item3"/>
</StackPanel>
<TextBlock Grid.Column="2" Margin="20,0,0,0" TextWrapping="Wrap" Text="This text should be shown based on the mouseovered button"/>
</Grid>
I was thinking to set the desired text to Tag of the Button but how to get that Tag to show on the mouse over event.
Notes:
I prefer to use individual Buttons here instead of ListBox or any other ItemsControl (the real application has more controls between these things etc.)
The application follows MVVM
I would prefer an approach other that setting the texts to UI directly (Tag of the Button)
For Mouse Move over,
Create an AttachedProperty for MouseMove and hook your list view with the property. The attached property can be used to any element in your application.
Attached Property
public class MouseBehaviour
{
public static readonly DependencyProperty MouseMoveCommandProperty =
DependencyProperty.RegisterAttached("MouseMoveCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseMoveCommandChanged)));
private static void MouseMoveCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseMove += new MouseEventHandler(element_MouseMove);
}
static void element_MouseMove(object sender, MouseEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseMoveCommand(element);
command.Execute(e);
}
public static void SetMouseMoveCommand(UIElement element, ICommand value)
{
element.SetValue(MouseMoveCommandProperty, value);
}
public static ICommand GetMouseMoveCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseMoveCommandProperty);
}
}
XAML
xmlns:mousebehav="clr-namespace:your namespace"
<Button mousebehav:MouseBehaviour.MouseMoveCommand="{Binding MouseMoveCommand}">
VM
private RelayCommand _MouseMoveCommand;
public RelayCommand MouseMoveCommand
{
get
{
if (_MouseMoveCommand== null) return _MouseMoveCommand= new RelayCommand(param => Execute_MouseMoveCommand((MouseEventArgs)param));
return _MouseMoveCommand;
}
set { _MouseMoveCommand= value; }
}
private void Execute_MouseMoveCommand(MouseEventArgs param)
{
//your logic to expand or ??
}
Attached property for Mouse Leave,
public static readonly DependencyProperty MouseLeaveCommandProperty =
DependencyProperty.RegisterAttached("MouseLeaveCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseLeaveCommandChanged)));
private static void MouseLeaveCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseLeave += new MouseEventHandler(element_MouseLeave);
}
static void element_MouseLeave(object sender, MouseEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseLeaveCommand(element);
command.Execute(e);
}
public static void SetMouseLeaveCommand(UIElement element, ICommand value)
{
element.SetValue(MouseLeaveCommandProperty, value);
}
public static ICommand GetMouseLeaveCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseLeaveCommandProperty);
}
VM
private RelayCommand _MouseLeaveCommand;
public RelayCommand MouseLeaveCommand
{
get
{
if (_MouseLeaveCommand== null) return _MouseLeaveCommand= new RelayCommand(param => Execute_MouseLeaveCommand((MouseEventArgs)param));
return _MouseLeaveCommand;
}
set { _MouseMoveCommand= value; }
}
private void Execute_MouseLeaveCommand(MouseEventArgs param)
{
//your logic to expand or ??
}
XAML
<Button mousebehav:MouseBehaviour.MouseLeaveCommand="{Binding MouseLeaveCommand}">
Related
Dependency property of my UserControl not trigger.My property inside usercontrol named "MultiColumnComboBox"=>
public readonly DependencyProperty ItemSourceValueProperty = DependencyProperty.Register("ItemSourceValue", typeof(List<EmployeeModel>), typeof(MultiColumnComboBox), new PropertyMetadata(new List<EmployeeModel>(),TargetPropertyChanged));
public List<EmployeeModel> ItemSourceValue
{
get
{
return (List<EmployeeModel>)GetValue(ItemSourceValueProperty);
}
set
{
SetValue(ItemSourceValueProperty, value);
}
}
private static void TargetPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
}
my xaml=>
<r:MultiColumnComboBox ItemSourceValue="{Binding EmployeeList}"
Grid.Row="0"
Grid.Column="1"
Margin="2"/>
I already check this link=>Link 1 Link 2 but this is not work for me.
I have created an attched property to bind the MouseUpCommand and the MouseDownCommand to my ViewModel. While the MouseDownCommand fires as it should, the MouseUpCommand only fires sporadically. Does anyone know what I am doing wrong?
Here is my XAML:
<ItemsControl ItemsSource="{Binding Page.Collection}" Grid.Row="1" Grid.Column="1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="Blue"
local:MouseBehaviour.MouseUpCommand="{Binding ViewModel.MouseUpCommand}"
local:MouseBehaviour.MouseDownCommand="{Binding ViewModel.MouseDownCommand}"
local:MouseBehaviour.MouseMoveCommand="{Binding ViewModel.MouseMoveCommand}">
</Canvas>
Here is the MouseBehavior:
public class MouseBehaviour
{
#region MouseUp
public static readonly DependencyProperty MouseUpCommandProperty =
DependencyProperty.RegisterAttached("MouseUpCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseUpCommandChanged)));
private static void MouseUpCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.PreviewMouseUp += element_MouseUp;
}
static void element_MouseUp(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseUpCommand(element);
command.Execute(e);
}
public static void SetMouseUpCommand(UIElement element, ICommand value)
{
element.SetValue(MouseUpCommandProperty, value);
}
public static ICommand GetMouseUpCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseUpCommandProperty);
}
#endregion
#region MouseDown
public static readonly DependencyProperty MouseDownCommandProperty =
DependencyProperty.RegisterAttached("MouseDownCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseDownCommandChanged)));
private static void MouseDownCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.PreviewMouseDown += element_MouseDown;
}
static void element_MouseDown(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseDownCommand(element);
command.Execute(e);
}
public static void SetMouseDownCommand(UIElement element, ICommand value)
{
element.SetValue(MouseDownCommandProperty, value);
}
public static ICommand GetMouseDownCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseDownCommandProperty);
}
#endregion
}
I have tried using the normal mouse commands as well as the preview mouse commands. No difference.
If I break the MVVM pattern and just add events to the code behind the program works flawlessly:
private void ItemsControl_MouseUp(object sender, MouseButtonEventArgs e)
{
mainViewModel.ViewModel.MouseUp(e);
}
private void ItemsControl_MouseMove(object sender, MouseEventArgs e)
{
mainViewModel.ViewModel.MouseMove(e);
}
private void ItemsControl_MouseDown(object sender, MouseButtonEventArgs e)
{
mainViewModel.ViewModel.MouseDown(e);
}
I'm using MVVM and have the following problem. My TextBox.Text is bound with UpdateSourceTrigger=LostFocus (thats what the user want). I have a Button with a SaveCommand CommandBinding - this works. Now i have a KeyBinding with Strg+S wich also execute the SaveCommand. And here is the problem: when i m in the Textbox and press Strg+s, the changes are not in the viewmodel.
is there any way to get MVVM Commands with KeyBinding and TextBox UpdateSourceTrigger=LostFocus working together?
some code to check out the problem
<Window>
<Window.InputBindings>
<KeyBinding Key="S" Modifiers="Control" Command="{Binding SaveCommand}"></KeyBinding>
</Window.InputBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding MyText1, UpdateSourceTrigger=LostFocus}" Width="100"></TextBox>
<Button Grid.Row="1" Content="_Save" Command="{Binding SaveCommand}" IsDefault="True"></Button>
</Grid>
</Window>
public partial class MainWindow : Window
{
private Viewmodel _data;
public MainWindow()
{
_data = new Viewmodel();
InitializeComponent();
this.DataContext = _data;
}
}
public class Viewmodel : INPCBase
{
private string _myText1;
private Lazy<DelegateCommand> _save;
public Viewmodel()
{
this._save = new Lazy<DelegateCommand>(()=> new DelegateCommand(this.SaveCommandExecute));
}
private void SaveCommandExecute()
{
MessageBox.Show(MyText1);
}
public string MyText1
{
get { return _myText1; }
set { _myText1 = value; this.NotifyPropertyChanged(()=>MyText1);}
}
public ICommand SaveCommand
{
get { return _save.Value; }
}
}
at the moment i came up with the following workaround. within the usercontrol/views where i define my KeyBindings, i also listen to the PreviewKeyDown event and set the focus to the next element when eg. Strg+S is pressed.
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
{
var fe = Keyboard.FocusedElement as UIElement;
if (fe != null)
{
fe.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
I have the same problem and end up with attached property for TextBox.
public static bool GetCommitOnSave(DependencyObject obj)
{
return (bool)obj.GetValue(CommitOnSaveProperty);
}
public static void SetCommitOnSave(DependencyObject obj, bool value)
{
obj.SetValue(CommitOnSaveProperty, value);
}
public static readonly DependencyProperty CommitOnSaveProperty =
DependencyProperty.RegisterAttached("CommitOnSave", typeof(bool), typeof(Helper), new PropertyMetadata(false, CommitOnSavePropertyChanged));
private static void CommitOnSavePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
if ((bool)e.NewValue)
{
if ((bool)e.NewValue)
{
textBox.KeyDown += TextBox_KeyDown;
}
else
{
textBox.KeyDown -= TextBox_KeyDown;
}
}
}
}
private static void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
var textBox = (TextBox)sender;
if (e.Key == Key.S && Keyboard.Modifiers == ModifierKeys.Control)
{
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
}
}
Using <TextBox Text="{Binding Name}" local:Helper.CommitOnSave="True" />
Of course you can set attached property in style for all TextBoxes in a form.
I think I find the best solution for me. I mix solution #blindmeis and my previous one with using attached property.
I create command which update binding source of actual keyboard focused element:
public class CommitValueCommand : ICommand
{
private static CommitValueCommand _instance;
public static CommitValueCommand Command => _instance ?? (_instance = new CommitValueCommand());
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (Keyboard.FocusedElement is TextBox textBox)
{
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
}
//for combobox etc.
else if (Keyboard.FocusedElement is Selector selector)
{
BindingOperations.GetBindingExpression(selector, Selector.SelectedValueProperty).UpdateSource();
}
}
}
In Execute method of command SaveCommand just at beginning invoke CommitValueCommand.Command.Execute().
I have a flyout panel (as illustrated below), which if undocked should be invisible when the mouse leaves the panel area in general.
However, I don't want the panel to close if any of these conditions occur:
1) The user opens a ContextMenu
2) The user chooses a ComboBox item that falls below the panel (as illustrated above)
3) A confirmation dialog that comes up due to a user action (such as deleting an item in the DataGrid)
It's easy to track context menu operations (ContextMenuOpening and ContextMenuClosing events) to handle the first case, but I haven't found any good ways yet to handle the other two cases, in particular tracking dialogs being opened.
Any ideas?
My flyout panel is just a grid whose visibility and content is determined in code behind:
<Grid Name="UndockedGrid" ContextMenuOpening="Grid_ContextMenuOpening" ContextMenuClosing="Grid_ContextMenuClosing" MouseLeave="Grid_MouseLeave">
<!-- Toolbox (undocked) -->
<ScrollViewer Name="ToolBoxUndockedViewer">
<StackPanel Name="ToolBoxUndockedPanel" />
</ScrollViewer>
</Grid>
I took a RoutedEvent approach to this problem, since my view models do not handle dialog workflows directly in this version.
To get the behavior I wanted for the data grid combo boxes, I extended DataGrid to add the routed events I wanted to track:
public class RoutableDataGrid : DataGrid
{
public static readonly RoutedEvent ElementOpenedEvent = EventManager.RegisterRoutedEvent("ElementOpened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutableDataGrid));
public event RoutedEventHandler ElementOpened
{
add { AddHandler(ElementOpenedEvent, value); }
remove { RemoveHandler(ElementOpenedEvent, value); }
}
public static readonly RoutedEvent ElementClosedEvent = EventManager.RegisterRoutedEvent("ElementClosed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutableDataGrid));
public event RoutedEventHandler ElementClosed
{
add { AddHandler(ElementClosedEvent, value); }
remove { RemoveHandler(ElementClosedEvent, value); }
}
public void RaiseElementOpened()
{
RaiseEvent(new RoutedEventArgs(RoutableDataGrid.ElementOpenedEvent, this));
}
public void RaiseElementClosed()
{
RaiseEvent(new RoutedEventArgs(RoutableDataGrid.ElementClosedEvent, this));
}
}
And the DataGridComboBoxColumn was extended to fire the new routed events:
public class BindableDataGridComboBoxColumn : DataGridComboBoxColumn
{
protected RoutableDataGrid ParentGrid { get; set; }
protected FrameworkElement Element { get; set; }
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
Element = base.GenerateEditingElement(cell, dataItem);
Element.MouseEnter += new System.Windows.Input.MouseEventHandler(element_MouseEnter);
Element.MouseLeave += new System.Windows.Input.MouseEventHandler(element_MouseLeave);
CopyItemsSource(Element);
return Element;
}
void element_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
if (ParentGrid != null)
{
ParentGrid.RaiseElementClosed();
}
}
void element_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
if (ParentGrid != null)
{
ParentGrid.RaiseElementOpened();
}
}
}
More of a hack, since MessageBox is a sealed class, I had to build a wrapper class to fire routed events using a MessageBox (and put an instance of this element in the xaml so that it's in the visual tree):
public class RoutedEventPlaceHolder : UIElement
{
public static readonly RoutedEvent ElementOpenedEvent = EventManager.RegisterRoutedEvent("ElementOpened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutedEventPlaceHolder));
public event RoutedEventHandler ElementOpened
{
add { AddHandler(ElementOpenedEvent, value); }
remove { RemoveHandler(ElementOpenedEvent, value); }
}
public static readonly RoutedEvent ElementClosedEvent = EventManager.RegisterRoutedEvent("ElementClosed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutedEventPlaceHolder));
public event RoutedEventHandler ElementClosed
{
add { AddHandler(ElementClosedEvent, value); }
remove { RemoveHandler(ElementClosedEvent, value); }
}
public void RaiseElementOpened()
{
RaiseEvent(new RoutedEventArgs(RoutedEventPlaceHolder.ElementOpenedEvent, this));
}
public void RaiseElementClosed()
{
RaiseEvent(new RoutedEventArgs(RoutedEventPlaceHolder.ElementClosedEvent, this));
}
public MessageBoxResult ShowMessageBox(string text, string caption, MessageBoxButton button)
{
RaiseElementOpened();
MessageBoxResult result = MessageBox.Show(text, caption, button);
RaiseElementClosed();
return result;
}
}
Then finally I can subscribe to the new routed events in the flyout panel:
<Grid Name="UndockedGrid" lib:RoutedEventPlaceHolder.ElementOpened="Grid_ElementOpened" lib:RoutableDataGrid.ElementOpened="Grid_ElementOpened" ContextMenuOpening="Grid_ContextMenuOpening" ContextMenuClosing="Grid_ContextMenuClosing" MouseLeave="Grid_MouseLeave">
<!-- Toolbox (undocked) -->
<ScrollViewer Grid.Row="0" Grid.Column="0" Name="ToolBoxUndockedViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Visibility="Collapsed" MouseLeave="ToolBoxUndockedViewer_MouseLeave">
<StackPanel Name="ToolBoxUndockedPanel" MinWidth="200" Background="{StaticResource ControlBackgroundBrush}" />
</ScrollViewer>
</Grid>
This is not an ideal solution. I'll accept any other answers that are more elegant.
How I can move focus from tabitem title to content of this tabitem by pressing downarrow?
I tried to use KeyboardNavigation but Keyboardfocus still doesn't move when I press down or up key.
Thanks in advance.
I created an attached property to address a similar problem. When you press a certain key on a ContentControl (TabItem), it focuses on the content. The XAML looks like this
<TabControl Focusable="False">
<TabItem Header="Main" local:Focus.ContentOn="Down">
<Stackpanel>
<TextBox />
</Stackpanel>
</TabItem>
And the Attached Property:
public static class Focus
{
public static Key GetContentOn(DependencyObject obj)
{
return (Key)obj.GetValue(ContentOnProperty);
}
public static void SetContentOn(DependencyObject obj, Key value)
{
obj.SetValue(ContentOnProperty, value);
}
// Using a DependencyProperty as the backing store for ContentOn. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentOnProperty =
DependencyProperty.RegisterAttached("ContentOn", typeof(Key), typeof(Navigate),
new FrameworkPropertyMetadata(Key.None, OnContentOnChanged));
private static void OnContentOnChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var control = o as ContentControl;
if (control != null)
control.KeyDown += FocusContent;
}
private static void FocusContent(object sender, KeyEventArgs e)
{
if (Keyboard.FocusedElement == sender)
{
var control = sender as ContentControl;
if (control != null && control.HasContent && GetContentOn(control) == e.Key)
{
((FrameworkElement)control.Content).MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
e.Handled = true;
}
}
}
}