My application has about 15 different UserControls that are uniformly loaded into a content-area at runtime.
My project is 100% MVVM-compliant, so I have the following XAML inserted into every UserControl's XAML:
<UserControl
...
xmlns:intr="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
>
<intr:Interaction.Triggers>
<intr:EventTrigger EventName="Loaded">
<intr:InvokeCommandAction Command="{Binding ViewLoadedCommand}"/>
</intr:EventTrigger>
</intr:Interaction.Triggers>
<!-- Rest of UserControl content here -->
</UserControl>
Obviously this isn't ideal and is also a violation of DRY.
What is the best way to have this XAML applied to this set of UserControls? (but not every UserControl, so a simple <Style Type="UserControl" /> would be inappropriate).
I use behavior implemented as attached property. It has two major advantages over System.Windows.Interactivity:
it can be defined in style.
much less xaml code in the views
in your case, the view could look like:
<UserControl ...
my:AttachedCommands.LoadedCommand="{Binding ViewLoadedCommand}">
In my solution, I'm not using commands, but I call methods on viewmodel if the viewmodel implements IViewModelLifeCycle interface:
public interface IViewModelLifeCycle
{
void Activate(object extraData);
void Deactivate();
}
All my views uses this style:
<Style x:Key="ViewBaseStyle">
<Setter Property="my:ViewModelLifeCycleBehavior.ActivateOnLoad" Value="True" />
and the behavior:
public static class ViewModelLifeCycleBehavior
{
public static readonly DependencyProperty ActivateOnLoadProperty = DependencyProperty.RegisterAttached("ActivateOnLoad", typeof (bool), typeof (ViewModelLifeCycleBehavior),
new PropertyMetadata(ActivateOnLoadPropertyChanged));
public static void SetActivateOnLoad(FrameworkElement element, bool value)
{
element.SetValue(ActivateOnLoadProperty, value);
}
public static bool GetActivateOnLoad(FrameworkElement element)
{
return (bool)element.GetValue(ActivateOnLoadProperty);
}
private static void ActivateOnLoadPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (DesignerProperties.GetIsInDesignMode(obj)) return;
var element = (FrameworkElement)obj;
element.Loaded -= ElementLoaded;
element.Unloaded -= ElementUnloaded;
if ((bool) args.NewValue == true)
{
element.Loaded += ElementLoaded;
element.Unloaded += ElementUnloaded;
}
}
static void ElementLoaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement) sender;
var viewModel = (IViewModelLifeCycle) element.DataContext;
if (viewModel == null)
{
DependencyPropertyChangedEventHandler dataContextChanged = null;
dataContextChanged = (o, _e) =>
{
ElementLoaded(sender, e);
element.DataContextChanged -= dataContextChanged;
};
element.DataContextChanged += dataContextChanged;
}
else if (element.ActualHeight > 0 && element.ActualWidth > 0) //to avoid activating twice since loaded event is called twice on TabItems' subtrees
{
viewModel.Activate(null);
}
}
private static void ElementUnloaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
var viewModel = (IViewModelLifeCycle)element.DataContext;
viewModel.Deactivate();
}
}
TIP:
Create your custom Item Template in Visual Studio for View and ViewModel. its very easy and saves a lot of time. The item template can contain xaml code with the trigger/behaviour, pointing to your base style, your d:DataContext definition and your viewmodel class.
you could create a "base-load" Usercontrol with the Interaction.Triggers and just put a ContentPresenter in it where you bind the real content to.
<UserControl x:class="OneOfMyOtherControls">
<MyBaseUserControl>
<!-- your UserControl Content-->
</MyBaseUserControl>
</UserControl>
Related
I have a ListView with pretty long listelement at times. I would like to create an event, where if I drag the mouse over an element, the whole name appears in a tooltip-like small window with the whole text of the item. This way the user can read it even if it is too long for the ListView window width.
I am a bit stuck, because I find no MouseOver event for the ListView elements. I would probably have to go on with a custom Style for my ListView, but I don't have experience with Styles.
I would really appreciate a little help, to get me started!
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"
<ListView 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 ??
}
Thanks for the answer. After a few hours of experimenting, I managed to solve it quite compact from the xaml:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="ToolTip" Value="{Binding Name}" />
</Style>
</ListView.ItemContainerStyle>
I am having a master window in which there are plenty of user control. and using navigation i am able to access the user controls. But by question is how to set focus on the first text box when ever the user control is opened.
I tried with dependency property and boolean flags, i was able to succeeded a bit. When i first render the UserControl i was able to focus but when i open for the second time i was not able to set focus on the TextBox.
And one more thing, i have validation for TextBoxes, if the validation fails then the textbox should be emptied and the focus should be on the respective text box.
How can i achieve this using MVVM in WPF (CLR 3.5, VS2008)
thanks in advance.
If you have a UserControl then you also have CodeBehind.
Place this inside your codebehind and you will do fine.
this.Loaded += (o, e) => { Keyboard.Focus(textBox1) }
Place this inside your UserControl XAML if you wish to listen to validation errors.
<UserControl>
<Grid Validation.Error="OnValidationError">
<TextBox Text{Binding ..., NotifyOnValidationError=true } />
</Grid>
<UserControl>
Inside the CodeBehind of your UserControl you will have something like this:
public void OnValidationError(o , args)
{
if(o is TextBox)
{
(TextBox)o).Text = string.Empty;
}
}
You should use AttachedProperty to stick to MVVM pattern it'll keep your view model independent of UI code and fully unit testable. Following attached property binds a boolean property to focus and highlight the TextBox, if you do not want the highlighting then you can remove the highlighting code and just work with focus code.
public class TextBoxBehaviors
{
#region HighlightTextOnFocus Property
public static readonly DependencyProperty HighlightTextOnFocusProperty =
DependencyProperty.RegisterAttached("HighlightTextOnFocus", typeof (bool), typeof (TextBoxBehaviors),
new PropertyMetadata(false, HighlightTextOnFocusPropertyChanged));
public static bool GetHighlightTextOnFocus(DependencyObject obj)
{
return (bool) obj.GetValue(HighlightTextOnFocusProperty);
}
public static void SetHighlightTextOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(HighlightTextOnFocusProperty, value);
}
private static void HighlightTextOnFocusPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
var uie = sender as UIElement;
if (uie == null) return;
if ((bool) e.NewValue)
{
uie.GotKeyboardFocus += OnKeyboardFocusSelectText;
uie.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownSetFocus;
}
else
{
uie.GotKeyboardFocus -= OnKeyboardFocusSelectText;
uie.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownSetFocus;
}
}
private static void OnKeyboardFocusSelectText(object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
textBox.SelectAll();
}
private static void OnMouseLeftButtonDownSetFocus(object sender, MouseButtonEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
if (!textBox.IsKeyboardFocusWithin)
{
textBox.Focus();
e.Handled = true;
}
}
#endregion
}
You can use this attached property in on your TextBox which you want to focus/highlight...
<TextBox ... local:TextBoxBehaviors.HighlightTextOnFocus="{Binding IsScrolledToEnd}" ... />
You can also try using FocusManager
<UserControl>
<Grid FocusManager.FocusedElement="{Binding Path=FocusedTextBox, ElementName=UserControlName}">
<TextBox x:Name="FocusedTextBox" />
</Grid>
<UserControl>
At a glance:
My app displays an ItemsControl containing a Canvas as its ItemsPanel. The ItemsControl is bound to a collection of objects, each having Left/Top/Width/Height properties. A DataTemplate is used to generate rectangles that are rendered in the Canvas and positioned correctly (binding on the Left and Top properties).
How can I implement drag/drop to move these rectangles around the Canvas?
Background for my question:
My WP7 app displays a "CanvasItemsControl" defined as follows:
public class CanvasItemsControl : ItemsControl
{
public string XBindingPath { get; set; }
public string YBindingPath { get; set; }
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
FrameworkElement contentitem = element as FrameworkElement;
if (XBindingPath != null && YBindingPath != null)
{
Binding xBinding = new Binding(XBindingPath);
Binding yBinding = new Binding(YBindingPath);
if (contentitem != null)
{
contentitem.SetBinding(Canvas.LeftProperty, xBinding);
contentitem.SetBinding(Canvas.TopProperty, yBinding);
}
}
base.PrepareContainerForItemOverride(element, item);
}
}
and used in XAML as follows:
<hh:CanvasItemsControl Grid.Row="1" x:Name="TheItemsControl"
Style="{StaticResource CanvasItemsControlStyle}"
ItemsSource="{Binding AllObjects}"
XBindingPath="Left" YBindingPath="Top" />
This is the style for the CanvasItemsControl:
<Style x:Key="CanvasItemsControlStyle" TargetType="local:CanvasItemsControl">
<Setter Property="ItemTemplate" Value="{StaticResource ObjectTemplate}"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
And this is the DataTemplate I use to render my class:
<DataTemplate x:Key="ObjectTemplate" >
<Border Background="{Binding Brush}"
Width="{Binding Width}"
Height="{Binding Height}">
<TextBlock Text="{Binding Description}"/>
</Border>
</DataTemplate>
The source of the CanvasItemsControl is a collection of objects that have the properties Left, Top, Width, Height, Brush, etc.
My question
As you can see, the end result is, as you add items to the AllObjects collection, each object gets rendered and positioned correctly in the canvas. Now I need to drag/drop/move these objects around the canvas. What approach would you advise me to use to implement drag/drop? Can you please guide me through the process?
Thank you
Here's the solution to my question (at least the best one in my opinion):
1) Use of a regular Canvas as opposed of a custom control inherited from Canvas.
2) Use of a user control taking the data context (the instance of my business entity) via constructor
3) The binding between the Left/Top properties of my business class and the Canvas.Left/Top is declared at the UserControl level.
4) Use of a custom behavior inheriting from System.Windows.Interactivity.Behavior. This behavior is attached to the User Control.
I would like to acknowlege Calvin Schrotenboer and Joe Gershgorin for their immense help.
<!--____ The UserControl ____-->
<UserControl... Canvas.Left={Binding Left}" Canvas.Top={Binding Top}">
<Grid.... layout of the UserControl instead of using a DataTemplate/>
<i:Interaction.Behaviors>
<MyExample:MyMouseDragElementBehavior/>
</i:Interaction.Behaviors>
</UserControl>
The custom behavior:
public class MyMouseDragElementBehavior : Behavior<FrameworkElement>
{
public event MouseEventHandler DragBegun;
public event MouseEventHandler DragFinished;
public event MouseEventHandler Dragging;
private Point relativePosition;
public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled", typeof(bool), typeof(MyMouseDragElementBehavior), new PropertyMetadata(true));
public bool IsEnabled
{
get
{
return (bool)GetValue(IsEnabledProperty);
}
set
{
SetValue(IsEnabledProperty, value);
}
}
protected override void OnAttached()
{
AssociatedObject.AddHandler(
UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), false);
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.RemoveHandler(
UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown));
base.OnDetaching();
}
private static int zIndex = 0;
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!IsEnabled)
{
return;
}
zIndex++;
Canvas.SetZIndex(AssociatedObject, zIndex);
StartDrag(e.GetPosition(AssociatedObject));
if (DragBegun != null)
{
DragBegun(this, e);
}
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
AssociatedObject.ReleaseMouseCapture();
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
HandleDrag(e.GetPosition(AssociatedObject));
if (Dragging != null)
{
Dragging(this, e);
}
}
internal void HandleDrag(Point newPositionInElementCoordinates)
{
double x = newPositionInElementCoordinates.X - relativePosition.X;
double y = newPositionInElementCoordinates.Y - relativePosition.Y;
if (AssociatedObject != null)
{
var currentLeft = Canvas.GetLeft(AssociatedObject);
var currentTop = Canvas.GetTop(AssociatedObject);
Canvas.SetLeft(AssociatedObject, currentLeft + x);
Canvas.SetTop(AssociatedObject, currentTop + y);
}
}
internal void StartDrag(Point positionInElementCoordinates)
{
relativePosition = positionInElementCoordinates;
AssociatedObject.CaptureMouse();
AssociatedObject.MouseMove += OnMouseMove;
AssociatedObject.LostMouseCapture += OnLostMouseCapture;
AssociatedObject.AddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp), false);
}
internal void EndDrag()
{
AssociatedObject.MouseMove -= OnMouseMove;
AssociatedObject.LostMouseCapture -= OnLostMouseCapture;
AssociatedObject.RemoveHandler(
UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp));
}
private void OnLostMouseCapture(object sender, MouseEventArgs e)
{
EndDrag();
if (DragFinished != null)
{
DragFinished(this, e);
}
}
}
I'm using the WPF datagrid, and have SelectionUnit="Cell" and SelectionMode="Extended". I'm also trying to adhere to the MVVM principals as much as I can.
I need my ViewModel to keep track of the current SelectedCells.
Life would be easy if I could just Bind its SelectedCells property to my ViewModel. Oddly enough, SelectedCells is only raised once - when we first select any cell in the grid.
MS explains it here: http://social.msdn.microsoft.com/Forums/en/wpf/thread/737117f4-6d20-4232-88cf-e52cc44d4431
Can anyone think of an MVVM-friendly approach to get around it?
Thanks!
I realized my last answer was for SelectedItems instead of SelectedCells, so I wrote a complete attached property class to do data binding for multiple SelectedCells which works as follows:
<controls:DataGrid ItemsSource="{StaticResource list}"
SelectionMode="Extended"
behaviors:DataGridSelectedCellsBehavior.SelectedCells="{Binding Path=SelectedGridCellCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
I have a working source code and a demo project of it here.
Attached property behavior Code :
public class DataGridSelectedCellsBehavior
{
// Source : https://archive.codeplex.com/?p=datagridthemesfromsl
// Credit to : T. Webster, https://stackoverflow.com/users/266457/t-webster
public static IList<DataGridCellInfo> GetSelectedCells(DependencyObject obj)
{
return (IList<DataGridCellInfo>)obj.GetValue(SelectedCellsProperty);
}
public static void SetSelectedCells(DependencyObject obj, IList<DataGridCellInfo> value)
{
obj.SetValue(SelectedCellsProperty, value);
}
public static readonly DependencyProperty SelectedCellsProperty = DependencyProperty.RegisterAttached("SelectedCells", typeof(IList<DataGridCellInfo>), typeof(DataGridSelectedCellsBehavior), new UIPropertyMetadata(null, OnSelectedCellsChanged));
static SelectedCellsChangedEventHandler GetSelectionChangedHandler(DependencyObject obj)
{
return (SelectedCellsChangedEventHandler)obj.GetValue(SelectionChangedHandlerProperty);
}
static void SetSelectionChangedHandler(DependencyObject obj, SelectedCellsChangedEventHandler value)
{
obj.SetValue(SelectionChangedHandlerProperty, value);
}
static readonly DependencyProperty SelectionChangedHandlerProperty = DependencyProperty.RegisterAttached("SelectedCellsChangedEventHandler", typeof(SelectedCellsChangedEventHandler), typeof(DataGridSelectedCellsBehavior), new UIPropertyMetadata(null));
//d is MultiSelector (d as ListBox not supported)
static void OnSelectedCellsChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
if (GetSelectionChangedHandler(d) != null)
return;
if (d is DataGrid)//DataGrid
{
DataGrid datagrid = d as DataGrid;
SelectedCellsChangedEventHandler selectionchanged = null;
foreach (var selected in GetSelectedCells(d) as IList<DataGridCellInfo>)
datagrid.SelectedCells.Add(selected);
selectionchanged = (sender, e) =>
{
SetSelectedCells(d, datagrid.SelectedCells);
};
SetSelectionChangedHandler(d, selectionchanged);
datagrid.SelectedCellsChanged += GetSelectionChangedHandler(d);
}
//else if (d is ListBox)
//{
// ListBox listbox = d as ListBox;
// SelectionChangedEventHandler selectionchanged = null;
// selectionchanged = (sender, e) =>
// {
// SetSelectedCells(d, listbox.SelectedCells);
// };
// SetSelectionChangedHandler(d, selectionchanged);
// listbox.SelectionChanged += GetSelectionChangedHandler(d);
//}
}
}
View Model Code :
class DemoViewModel : INotifyPropertyChanged
{
private IList<DataGridCellInfo> selectedGridCellCollection = new List<DataGridCellInfo>();
public IList<DataGridCellInfo> SelectedGridCellCollection
{
get { return selectedGridCellCollection; }
set
{
selectedGridCellCollection = value;
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Do you need the SelectedCells constantly data-binded, or just when the user hits the OK/Accept button? If you only need it at the end of whatever process the user is in you can bind the SelectedCells to the CommandParameter property of a Button, for example. The SelectedCells is an IList, and you know enough to just do a cast to whatever object type the selection actually is. The other option is messier, you can use an attached property, keeping the event-handling out of your Views. This attached property would handle either a ListBox or in your case a DataGrid (MultiSelector).
public class Attach
{
public static IList GetSelectedItems(DependencyObject obj)
{
return (IList)obj.GetValue(SelectedItemsProperty);
}
public static void SetSelectedItems(DependencyObject obj, IList value)
{
obj.SetValue(SelectedItemsProperty, value);
}
/// <summary>
/// Attach this property to expose the read-only SelectedItems property of a MultiSelector for data binding.
/// </summary>
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(Attach), new UIPropertyMetadata(new List<object>() as IList, OnSelectedItemsChanged));
static SelectionChangedEventHandler GetSelectionChangedHandler(DependencyObject obj)
{
return (SelectionChangedEventHandler)obj.GetValue(SelectionChangedHandlerProperty);
}
static void SetSelectionChangedHandler(DependencyObject obj, SelectionChangedEventHandler value)
{
obj.SetValue(SelectionChangedHandlerProperty, value);
}
static readonly DependencyProperty SelectionChangedHandlerProperty =
DependencyProperty.RegisterAttached("SelectionChangedHandler", typeof(SelectionChangedEventHandler), typeof(Attach), new UIPropertyMetadata(null));
//d is MultiSelector (d as ListBox not supported)
static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
if (GetSelectionChangedHandler(d) != null)
return;
if (d is MultiSelector)//DataGrid
{
MultiSelector multiselector = d as MultiSelector;
SelectionChangedEventHandler selectionchanged = null;
foreach (var selected in GetSelectedItems(d) as IList)
multiselector.SelectedItems.Add(selected);
selectionchanged = (sender, e) =>
{
SetSelectedItems(d, multiselector.SelectedItems);
};
SetSelectionChangedHandler(d, selectionchanged);
multiselector.SelectionChanged += GetSelectionChangedHandler(d);
}
else if (d is ListBox)
{
ListBox listbox = d as ListBox;
SelectionChangedEventHandler selectionchanged = null;
selectionchanged = (sender, e) =>
{
SetSelectedItems(d, listbox.SelectedItems);
};
SetSelectionChangedHandler(d, selectionchanged);
listbox.SelectionChanged += GetSelectionChangedHandler(d);
}}}
Usage in XAML:
<DataGrid ItemsSource="{Binding Path=SourceList}"
myControls:Attach.SelectedItems="{Binding Path=myMvvmSelectedItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Extended" />
You might be interested in the BookLibrary sample application of the WPF Application Framework (WAF). It shows how to synchronize the DataGrid.SelectedItems with the ViewModel. This might be very similar to SelectedCells.
Somewhere between perfect MVVM bindings and full event handler codebehinds there is the gray area of interactivity EventTriggers (see Blend SDK) :)
If you put an eventtrigger to the datagrid, and set to "SelectionChanged" and pass the eventargs to a command (use an EventToCommand actiontrigger) you could get the selected items from the eventargs hopefully...
Or use the multibinding as said in the MS thread :)
I want to create a Style for a WPF ListBox that includes a Button in the ControlTemplate that the user can click on and it clears the ListBox selection.
I dont want to use codebehind so that this Style can be applied to any ListBox.
I have tried using EventTriggers and Storyboards and it has proved problematic as it only works first time and stopping the Storyboard sets the previous selection back.
I know I could use a user control but I want to know if it is possible to achieve this using only a Style.
It is not possible to achieve this using XAML and only the classes provided by the .NET framework. However you can still produce a reusable solution by defining a new command (call it ClearSelectionCommand) and a new attached property (call it ClearSelectionOnCommand).
Then you can incorporate those elements into your style.
Example:
public class SelectorBehavior
{
public static RoutedCommand
ClearSelectionCommand =
new RoutedCommand(
"ClearSelectionCommand",
typeof(SelectorBehavior));
public static bool GetClearSelectionOnCommand(DependencyObject obj)
{
return (bool)obj.GetValue(ClearSelectionOnCommandProperty);
}
public static void SetClearSelectionOnCommand(
DependencyObject obj,
bool value)
{
obj.SetValue(ClearSelectionOnCommandProperty, value);
}
public static readonly DependencyProperty ClearSelectionOnCommandProperty =
DependencyProperty.RegisterAttached(
"ClearSelectionOnCommand",
typeof(bool),
typeof(SelectorBehavior),
new UIPropertyMetadata(false, OnClearSelectionOnCommandChanged));
public static void OnClearSelectionOnCommandChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Selector selector = d as Selector;
if (selector == null) return;
bool nv = (bool)e.NewValue, ov = (bool)e.OldValue;
if (nv == ov) return;
if (nv)
{
selector.CommandBindings.Add(
new CommandBinding(
ClearSelectionCommand,
ClearSelectionCommand_Executed,
ClearSelectionCommand_CanExecute));
}
else
{
var cmd = selector
.CommandBindings
.Cast<CommandBinding>()
.SingleOrDefault(x =>
x.Command == ClearSelectionCommand);
if (cmd != null)
selector.CommandBindings.Remove(cmd);
}
}
public static void ClearSelectionCommand_Executed(
object sender,
ExecutedRoutedEventArgs e)
{
Selector selector = (Selector)sender;
selector.SelectedIndex = -1;
}
public static void ClearSelectionCommand_CanExecute(
object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
}
Example usage - XAML:
<Window x:Class="ClearSelectionBehaviorLibrary.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClearSelectionBehaviorLibrary"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style x:Key="MyStyle" TargetType="Selector">
<Setter
Property="local:SelectorBehavior.ClearSelectionOnCommand"
Value="True"/>
</Style>
</Window.Resources>
<Grid>
<DockPanel>
<Button
DockPanel.Dock="Bottom"
Content="Clear"
Command="{x:Static local:SelectorBehavior.ClearSelectionCommand}"
CommandTarget="{Binding ElementName=TheListBox}"/>
<ListBox
Name="TheListBox"
ItemsSource="{Binding MyData}"
Style="{StaticResource MyStyle}"/>
</DockPanel>
</Grid>
</Window>
Example usage - Code Behind:
public partial class Window1 : Window
{
public List<string> MyData { get; set; }
public Window1()
{
MyData = new List<string>
{
"aa","bb","cc","dd","ee"
};
InitializeComponent();
DataContext = this;
}
}