How edit repeatButton Interval? - c#

A have ListBox and 4 Items.
2 visible
2 colpased:
Click:
-this bad!
I need this:
I need Set in reapeatButton change Interval!?!? how to do it

What you want is for the list box to scroll by two lines for every one time you click the repeat buttons. Here is a behavior that you can add to your ListBox that will do just that.
First add this namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
and the corresponding reference to your project.
Then the XAML looks like this:
<ListBox ScrollViewer.VerticalScrollBarVisibility="Visible" Height="40">
<i:Interaction.Behaviors>
<local:ScrollBehavior LineMultiplier="2"/>
</i:Interaction.Behaviors>
<ListBoxItem Content="Item1"/>
<ListBoxItem Content="Item2"/>
<ListBoxItem Content="Item3"/>
<ListBoxItem Content="Item4"/>
</ListBox>
and here is the behavior:
class ScrollBehavior : Behavior<FrameworkElement>
{
public int LineMultiplier
{
get { return (int)GetValue(LineMultiplierProperty); }
set { SetValue(LineMultiplierProperty, value); }
}
public static readonly DependencyProperty LineMultiplierProperty =
DependencyProperty.Register("LineMultiplier", typeof(int), typeof(ScrollBehavior), new UIPropertyMetadata(1));
protected override void OnAttached()
{
AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
}
private ScrollViewer scrollViewer;
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
scrollViewer = GetScrollViewer(AssociatedObject);
scrollViewer.CommandBindings.Add(new CommandBinding(ScrollBar.LineUpCommand, LineCommandExecuted));
scrollViewer.CommandBindings.Add(new CommandBinding(ScrollBar.LineDownCommand, LineCommandExecuted));
}
private void LineCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (e.Command == ScrollBar.LineUpCommand)
{
for (int i = 0; i < LineMultiplier; i++)
scrollViewer.LineUp();
}
if (e.Command == ScrollBar.LineDownCommand)
{
for (int i = 0; i < LineMultiplier; i++)
scrollViewer.LineDown();
}
}
private ScrollViewer GetScrollViewer(DependencyObject o)
{
if (o is ScrollViewer)
return o as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
{
var result = GetScrollViewer(VisualTreeHelper.GetChild(o, i));
if (result != null)
return result;
}
return null;
}
}

I had an application which required me to scroll per item so setting scrollviewer.isvirtualizing to true was enough.
However, I needed to implement a similar behavior with a dockpanel, so I used Rick Sladkey's method to accomplish what I needed.

Related

Get Value from Custom Control in DataGridTemplateColumn

I am modying from legacy code that is using Grid_CellEditEnding - I modified the grid to use a custom control:
<DataGridTemplateColumn x:Name="cellQty" Header="Qty" Width="1.1*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Resources:NumericTextColumn Text="{Binding Qty}" >
</Resources:NumericTextColumn>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Resources:NumericTextColumn Text="{Binding Qty}" >
</Resources:NumericTextColumn>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
And once the cell event triggers I am trying to get the value of the object but am having difficulty casting the object (getting presentation framework errors)
private void Grid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if(e.Column.Header.ToString().Equals("Qty"))
{
//this is in error
var newQuantityText = ((NumericTextColumn)e.EditingElement).Text;
}
}
I have used the DataContext Property of the row and casted with Class Type to get Property. Instead of Data Class you need to provide your Class which has Qty Property.
private void Grid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if(e.Column.Header.ToString().Equals("Qty"))
{
var d = ((Data)e.Row.DataContext).Qty;
}
}
Updated Answer
In order to get the current entered data, in the grid row.
private void Grid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if(e.Column.Header.ToString().Equals("Qty"))
{
var NumericDataCtrl= GetVisualChild<NumericTextColumn>(e.EditingElement);
var data = NumericDataCtrl.Text;
}
}
To access the child Control inside EditingElement, I used the below method
public static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>
(v);
}
if (child != null)
{
break;
}
}
return child;
}

How to delete selected rows (using checkbox) in wpf datagrid

My WPF DataGrid is:
<dg:DataGrid Name="datagrid1" Grid.RowSpan="1" VerticalAlignment="Stretch" Grid.ColumnSpan="2">
<dg:DataGrid.Columns >
<dg:DataGridTemplateColumn>
<dg:DataGridTemplateColumn.Header>
<CheckBox Content=" Slect All" x:Name="headerCheckBox" />
</dg:DataGridTemplateColumn.Header>
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="chkSelectAll" Margin="45 2 0 0"
IsChecked="{Binding IsChecked, ElementName=headerCheckBox,
Mode=OneWay}" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
</dg:DataGrid.Columns>
</dg:DataGrid>
Also Dynamicaly I am populating the data to the datgrid.In xaml.cs file I written the below given code for deleting the selected row from the data grid but it throwing the error at line
DataGridRow item =(DataGridRow) datagrid1.ItemContainerGenerator.ContainerFromItem(datagrid1.Items[j]);
So Please have a look in to the below given code which I written for doing the same.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
for (int j = 0; j < datagrid1.Items.Count; j++)
{
DataGridRow item =(DataGridRow) datagrid1.ItemContainerGenerator.ContainerFromItem(datagrid1.Items[j]);
CheckBox ckb = (CheckBox)GetVisualChild<CheckBox>(item);
if (ckb.IsChecked.Value)
{
DataRowView drv = (DataRowView)datagrid1.Items[j];
//delete the row- updating to the database
}
}
}
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
Please let me know if am wrong.
Here is how I would do this. Implement an ObservableCollection of your class that inherits INotifyPropertyChanged. INotifyPropertyChanged will be used in case we want to update items in the collection.
First the xaml for the GridView
<DataGrid x:Name="gvMain" AutoGenerateColumns="True" HorizontalAlignment="Left"
VerticalAlignment="Top" Height="300" Width="300"></DataGrid>
Our class of items
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string firstName { get; set; }
private string lastName { get; set; }
public string FirstName
{
get
{
return firstName;
}
set
{
firstName = value;
PropertyChangedEvent("FirstName");
}
}
public string LastName
{
get
{
return lastName;
}
set
{
lastName = value;
PropertyChangedEvent("LastName");
}
}
private void PropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Next the WPF window
public ObservableCollection<MyClass> gridData { get; set; }
public MainWindow()
{
InitializeComponent();
gridData = new ObservableCollection<MyClass>();
gvMain.ItemsSource = gridData;
}
Test to add, change, delete items in the collection
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
gridData.Add(new MyClass() { FirstName = "John", LastName = "Smith" });
}
private void btnChange_Click(object sender, RoutedEventArgs e)
{
gridData[0].FirstName = "Meow Mix";
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
//using List to use .ForEach less code to write and looks cleaner to me
List<MyClass> remove = gridData.Where(x => x.LastName.Equals("Smith")).ToList();
remove.ForEach(x => gridData.Remove(x));
}
Any changes you want to make will be done with gridData.

WPF Listview item highlighting

I have a listview which contains the customer informations. There is a search text box above the that listview. When you type anything into the textbox then it higlights the matched item in the listview. But , the problem is that it makes search only in the visual side of the listview. It doesn't search in the not scrolled side of the listview(buttom of the listview). My code is below. Please have a look.
private void FindListViewItem(DependencyObject obj)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
ListViewItem lv = obj as ListViewItem;
if (lv != null)
{
HighlightText(lv);
}
FindListViewItem(VisualTreeHelper.GetChild(obj as DependencyObject, i));
}
}
private void HighlightText(Object itx)
{
if (itx != null)
{
if (itx is TextBlock)
{
Regex regex = new Regex("(" +TxtSearch.Text + ")", RegexOptions.IgnoreCase);
TextBlock tb = itx as TextBlock;
if (TxtSearch.Text.Length == 0)
{
string str = tb.Text;
tb.Inlines.Clear();
tb.Inlines.Add(str);
return;
}
string[] substrings = regex.Split(tb.Text);
tb.Inlines.Clear();
foreach (var item in substrings)
{
if (regex.Match(item).Success)
{
Run runx = new Run(item);
runx.Background = Brushes.Lime;
tb.Inlines.Add(runx);
if (tb.IsMouseOver)
{
tb.IsEnabled = false;
}
}
else
{
tb.Inlines.Add(item);
tb.IsEnabled = false;
}
}
return;
}
else
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(itx as DependencyObject); i++)
{
HighlightText(VisualTreeHelper.GetChild(itx as DependencyObject, i));
}
}
}
}
This happens because the ListView, by default, uses virtualization for its content. This means that the ListViewItems are created when they are needed. If you didn't scroll the ListView, some ListViewItems will not be created and VisualTreeHelper.GetChildrenCount will not be able to return those ListViewItems.
To achieve what you want, you can:
disable ListView virtualization by setting: VirtualizingStackPanel.IsVirtualizing="False" on your ListView (not recommended if you have many items in your list).
you can enforce the creation of the ListViewItem which are not visible by calling IItemContainerGenerator.GenerateNext and IItemContainerGenerator.PrepareItemContainer (not recommended at all). (also take a look at this)
find a better logic to highlight your ListViewItems :) (recommended). (for example search on your collection for the items you want to highlight instead of searching on the UI elements that are only displaying your items. Then mark the items found as highlighted and base on this, display the ListViewItems accordingly (with a different template or style))
You can do this in several ways. Here is one way that i think would work in your scenario and partially with your code and still use virtualization.
Use a data template for the list view item, and create an event handler for loaded event, something like:
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Loaded="FrameworkElement_OnLoaded"/>
</DataTemplate>
</ListView.ItemTemplate>
In the OnLoaded event handler call your HighlightText method on the sender:
HighlightText(sender)
In order to trigger the loaded event you'll need to refresh the list view each time the search string will change. Something like ListView.Items.Refresh() should do it.
You could improve this a bit by adding a small timer on the search text changed, so the user will be able to finish typing when it's searching for something.
There are other, more elegant ways to handle this, but for your case i think this should work.
In Addition to my Comment:
Use a Property and a Observable Collection and directly filter on that Collection.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Entry> MyCollection {get;set;}
public MainWindow()
{
InitializeComponent();
MyCollection = new ObservableCollection<Entry>();
MyCollection.Add(new Entry() { Name = "Test" });
MyCollection.Add(new Entry() { Name = "ABCD" });
MyCollection.Add(new Entry() { Name = "TESTABC" });
MyCollection.Add(new Entry() { Name = "BCDtest" });
this.MyListView.DataContext = this;
}
private void searchTerm_KeyUp(object sender, KeyEventArgs e)
{
String term = ((TextBox)sender).Text;
foreach (Entry entry in this.MyCollection)
{
if (entry.Name.Contains(term))
entry.Highlight();
else
entry.UnHighlight();
}
}
}
public class Entry : INotifyPropertyChanged
{
public String Name { get; set; }
public Color BGColor { get; set; }
public SolidColorBrush BGBrush
{
get
{
return new SolidColorBrush(this.BGColor);
}
}
public Entry()
{
this.UnHighlight();
}
public void Highlight()
{
this.BGColor = Colors.Yellow;
this.NotifyPropertyChanged("BGBrush");
}
public void UnHighlight()
{
this.BGColor = Colors.White;
this.NotifyPropertyChanged("BGBrush");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
along with
<Grid>
<DockPanel>
<TextBox DockPanel.Dock="Top" Name="searchTerm" KeyUp="searchTerm_KeyUp"></TextBox>
<ListView Name="MyListView" ItemsSource="{Binding MyCollection}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Background="{Binding BGBrush}" Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DockPanel>
</Grid>
And you are done. No need to manually touch the listview at any time. (To Increase Performance: For the Raising of the PropertyChanged Event you may want to add a check, if its really changing, or if it has been set to white from white etc.)

wpf treeview blues. I want to select an item

I'm trying to select a TreeViewItem. Now, I have access to the containing TreeViewItem and have told it to expand so I can select its kid. If it's already expanded all is well, if it's not then I run this code:
EventHandler selector = new EventHandler(delegate
{
if (selectedDirectoryTreeItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
TreeViewItem want = selectedDirectoryTreeItem.ItemContainerGenerator.ContainerFromItem(dirWeWantSelected) as TreeViewItem;
if (want == null)
return;
want.IsSelected = true;
// selectedDirectoryTreeItem.ItemContainerGenerator.StatusChanged -= selector;
}
});
selectedDirectoryTreeItem.ItemContainerGenerator.StatusChanged += selector;
So my question is, why wont it select? want is always null. I'm scouring the interwebs looking for another way of doing this but it would be cool if somebody could explain this to me
I've personally always found it easiest to stick a Selected property into my model object and then just bind the TreeViewItem Selected property to the Selected property of the model. Here is some code:
Model
public class Data : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Data()
{
DataItems = new List<Data>();
}
public string Name { get; set; }
private bool _selected;
public bool Selected
{
get { return _selected; }
set
{
_selected = value;
OnPropertyChanged("Selected");
}
}
public List<Data> DataItems { get; set; }
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:controls="clr-namespace:MyControls;assembly=MyControls"
Title="Window1">
<Window.Resources>
<Style x:Key="CustomTreeViewItem" TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding Path=Selected, Mode=TwoWay}" />
<Setter Property="IsExpanded" Value="True" />
</Style>
</Window.Resources>
<DockPanel Background="Transparent">
<TreeView x:Name="_tvTest" DockPanel.Dock="Left" ItemContainerStyle="{StaticResource CustomTreeViewItem}" Width="300" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Data}" ItemsSource="{Binding DataItems}">
<TextBlock Text="{Binding Name}" Padding="2" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Padding="2" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<Button Content="Select Random TreeView Item" Click="Button_Click" Height="50" Width="200" />
</DockPanel>
</Window>
Code behind
public partial class Window1 : Window
{
private Random _random;
private List<Data> _dataItems;
public Window1()
{
InitializeComponent();
_dataItems = Init();
_tvTest.ItemsSource = _dataItems;
_random = new Random(5);
}
private List<Data> Init()
{
List<Data> dataItems = new List<Data>();
for (int i = 1; i <= 10; i++)
{
Data d1 = new Data();
d1.Name = "Data:" + i.ToString();
for (int j = 1; j <= 4; j++)
{
Data d2 = new Data();
d2.Name = "Data:" + i.ToString() + j.ToString();
d1.DataItems.Add(d2);
}
dataItems.Add(d1);
}
return dataItems;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int index = _random.Next(0, 9);
int subIndex = _random.Next(0, 3);
if (subIndex == 0)
_dataItems[index].Selected = true;
else
_dataItems[index].DataItems[subIndex - 1].Selected = true;
}
}
In my opinion, this is a bug in WPF, but I have run into the same issue several times. I never trust the ItemContainerGenerator's Status and instead loop on separate thread as such:
private void _selectTreeViewHelper(object directory) {
TreeViewItem want = null;
bool broke = false; //probably some sort of wait timeout instead, but works for sake of example
while (true) {
Dispatcher.Invoke(new Action(delegate {
want = selectedDirectoryTreeItem.ItemContainerGenerator.ContainerFromItem(directory) as TreeViewItem;
if (want != null && want.IsLoaded) {
want.IsSelected = true;
broke = true;
}
}));
if (broke) { break; }
Thread.Sleep(100);
}
}
Then call it:
var thread = new Thread(new ParameterizedThreadStart(_selectTreeViewHelper));
thread.Start(dirWeWantSelected);
Pain I know, but it works.
Thanks for the help, I figured it out. Well it works anyway but i'm not entirely sure why it didn't before... if anyone can tell me it would be lovely... I kinda combined elements of the two responses above and then added on a little bit to make it work. For some reason, no matter what i do I cant select the TVI (TreeViewElement) if its parent wasn't already expanded, even if the parent TVI said its contents were generated and, in fact, had contents i could iterate through. My solution was thus to wait for the contents to be generated and then itrate through them and find the one i wanted. It's really weird to me that I couldn't just grab a container given its contents. Meh. My code could stand to be refactored a little bit but here it is: (works perfectly)
public void listItemClickClick(object sender, RoutedEventArgs e)
{
try
{
UserFile fil = (UserFile)(sender as ListBoxItem).DataContext;
MessageBox.Show("to do: download stuff");
return;
}
catch (InvalidCastException)
{
}
try
{
dirWeWantSelected = (Directory)(sender as ListBoxItem).DataContext;
}
catch (InvalidCastException)
{
MessageBox.Show("this should never happen");
}
selectedDirectoryTreeItem.IsExpanded = true;
TreeViewItem want = null;
try
{
want = selectedDirectoryTreeItem.ItemContainerGenerator.ContainerFromItem(dirWeWantSelected) as TreeViewItem;
}
catch
{
MessageBox.Show("weird error");
}
if (want != null)
{
want.IsSelected = true;
}
else
{
selectedDirectoryTreeItem.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
}
void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (selectedDirectoryTreeItem.ItemContainerGenerator.Status
== System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
selectedDirectoryTreeItem.ItemContainerGenerator.StatusChanged
-= ItemContainerGenerator_StatusChanged;
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
new Action(DelayedAction));
}
}
void DelayedAction()
{
selectedDirectoryTreeItem.Items.MoveCurrentToFirst();
Directory curr;
do
{
curr = (Directory)selectedDirectoryTreeItem.Items.CurrentItem;
if (curr.id == dirWeWantSelected.id)
{
curr.Selected = true;
return;
}
selectedDirectoryTreeItem.Items.MoveCurrentToNext();
}
while (selectedDirectoryTreeItem.Items.CurrentItem != null);
}

How to catch Click on ListboxItem when the item is templated?

I have a ListBox with ItemsTemplate set.
Now I want to catch Ctrl + Left click on a ListBoxItem.
I found KeyBoard class that should give me modifier keys. Now how do I get the click event on the ListBoxItem? Even better, how do I bind it to ICommand.
I found some bits and pieces but don't know how to connect them. It seems InputBinding seems could help me or EventSetter.
Below is a simple example that handles Ctrl + PreviewMouseLeftButtonDown using an EventSetter in the ListBoxItem's Style. This is probably what you want.
XAML:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListBoxItem_PreviewMouseLeftButtonDown"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<s:String>Item1</s:String>
<s:String>Item2</s:String>
<s:String>Item3</s:String>
<s:String>Item4</s:String>
</ListBox>
Code-Behind:
void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) > 0)
{
Console.WriteLine((sender as ListBoxItem).Content.ToString());
e.Handled = false;
}
}
To bind it to an ICommand, you can use an attached behavior like the EventToCommand behavior discussed here.
EDIT:
To address your comment, handling the Click-event will be a bit tricky for the ListBoxItem because of two things: 1) The ListBoxItem doesn't have a click event, and 2) The ListBoxItem internally handles some of the MouseEvents. Anyway, I came up with a simulated, attached ClickEvent to make it work. See below. Hope it works.
public class AttachedEvents
{
private static readonly DependencyProperty IsTriggerEnabledProperty =
DependencyProperty.RegisterAttached("IsTriggerEnabled", typeof(bool), typeof(FrameworkElement), new FrameworkPropertyMetadata(false));
public static readonly RoutedEvent ClickEvent;
static AttachedEvents()
{
try
{
ClickEvent = EventManager.RegisterRoutedEvent("Click",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(FrameworkElement));
}
catch (Exception ex)
{ }
}
private static void SetIsTriggerEnabled(FrameworkElement element, bool value)
{
if (element != null)
{
element.SetValue(IsTriggerEnabledProperty, value);
}
}
private static bool GetIsTriggerEnabled(FrameworkElement element)
{
return (element != null) ? (bool)element.GetValue(IsTriggerEnabledProperty) : false;
}
public static void AddClickHandler(DependencyObject o, RoutedEventHandler handler)
{
FrameworkElement element = (FrameworkElement)o;
element.AddHandler(ClickEvent, handler);
element.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(SimulatedClick_MouseLeftButtonUp);
element.PreviewMouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(SimulatedClick_MouseLeftButtonDown);
}
public static void RemoveClickHandler(DependencyObject o, RoutedEventHandler handler)
{
FrameworkElement element = (FrameworkElement)o;
element.RemoveHandler(ClickEvent, handler);
element.MouseLeftButtonUp -= new System.Windows.Input.MouseButtonEventHandler(SimulatedClick_MouseLeftButtonUp);
element.PreviewMouseLeftButtonDown -= new System.Windows.Input.MouseButtonEventHandler(SimulatedClick_MouseLeftButtonDown);
}
static void SimulatedClick_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
UpdateIsTriggerSet(element);
Mouse.Capture(element);
}
static void SimulatedClick_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
bool isTriggerSet = (bool)element.GetValue(IsTriggerEnabledProperty);
// update the trigger set flag
UpdateIsTriggerSet(element);
//release the mouse capture
Mouse.Capture(null);
// if trigger is set and we are still over the element then we fire the click event
if (isTriggerSet && IsMouseOver(element))
{
element.RaiseEvent(new RoutedEventArgs(ClickEvent, sender));
}
}
private static bool IsMouseOver(FrameworkElement element)
{
Point position = Mouse.PrimaryDevice.GetPosition(element);
if (((position.X >= 0.0) && (position.X <= element.ActualWidth)) && ((position.Y >= 0.0) && (position.Y <= element.ActualHeight)))
{
return true;
}
else
{
return false;
}
}
private static void UpdateIsTriggerSet(FrameworkElement element)
{
Point position = Mouse.PrimaryDevice.GetPosition(element);
if (((position.X >= 0.0) && (position.X <= element.ActualWidth)) && ((position.Y >= 0.0) && (position.Y <= element.ActualHeight)))
{
if (!(bool)element.GetValue(IsTriggerEnabledProperty))
{
element.SetValue(IsTriggerEnabledProperty, true);
}
}
else if ((bool)element.GetValue(IsTriggerEnabledProperty))
{
element.SetValue(IsTriggerEnabledProperty, false);
}
}
}
Sample usage is shown below. I can't seem to set the attached event in XAML (I'm not sure why) so I had to do a workaround here. What I do is wait 'til the ListBoxItem gets loaded and Attach the event handler in the code-behind.
XAML:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="Loaded" Handler="OnLoaded"/>
</Style>
</ListBox.ItemContainerStyle>
...
</ListBox>
Code-Behind:
void OnLoaded(object sender, RoutedEventArgs e)
{
AttachedEvents.AddClickHandler((sender as ListBoxItem), HandleClick);
}
void HandleClick(object sender, RoutedEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) > 0)
{
Console.WriteLine("Ctrl + Clicked!");
}
}

Categories