Accessing to a ListView at runtime to update an item - wpf

I have to update a ListView item by clicking on a button. How do I find and update one at the runtime?
update: I mean I have to find the certain ListView item and update the text of this item only.

When ListViewItems were added to the ListView manually, you can look them up by their content and replace with new content like this (using System.Linq):
object contentToReplace = ...;
object newContent = ...;
ListViewItem item = listView.Items.Cast<ListViewItem>().FirstOrDefault(
lvi => lvi.Content == contentToReplace);
if (item != null)
{
item.Content = newContent;
}

You may use commands. For example:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public static readonly ICommand ItemClickCommand = new RoutedCommand("ItemClick", typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
this.CommandBindings.Add(
new CommandBinding(
MainWindow.ItemClickCommand,
this.ExecuteItemClickCommand,
this.CanExecuteItemClickCommand));
}
private void CanExecuteItemClickCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = e.Parameter is ListBoxItem;
}
private void ExecuteItemClickCommand(object sender, ExecutedRoutedEventArgs e)
{
// Here you can access ListBoxItem that holds a clicked button.
ListBoxItem listBoxItem = (ListBoxItem)e.Parameter;
listBoxItem.Content = "...";
}
}
}
Now, the only thing you need is to assign ItemClickCommand to a button and bind CommandParameter to corresponding ListBoxItem.
XAML example:
<Window ...
xmlns:local="clr-namespace:WpfApplication1">
<ListBox>
<ListBoxItem>
<ListBoxItem.Content>
<Button Command="{x:Static local:MainWindow.ItemClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Content="Click Me"/>
</ListBoxItem.Content>
<...>

Related

WPF: ListViewIndex from PreviewMouseLeftButtonDown event using command

I want to get index of ListViewIndex from PreviewMouseLeftButtonDown event using command:
<ListView Name="ListViewFiles">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding ListViewItemMouseLeftButtonDownCommand}"
CommandParameter="{Binding ElementName=ListViewFiles, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
Code:
Here i have my ListView and i cannot find a way to get my ListViewItem index or object.
I try SelectedItem but its null
public void Execute(object parameter)
{
var listView = parameter as ListView;
}
PreviewMouseLeftButtonDown is triggered before the item is selected so this approach of using an EventTrigger won't work.
You could hook up an event handler to the MouseLeftButtonDownEvent using the AddHandler method and the handledEventsToo parameter in the code-behind of the view:
ListViewFiles.AddHandler(ListView.MouseLeftButtonDownEvent, new RoutedEventHandler((ss, ee) =>
{
(DataContext as YourViewModel).ListViewItemMouseLeftButtonDownCommand.Execute(ListViewFiles.SelectedItem);
}), true);
This is not any worse than using an EventTrigger in the XAML markup as far as MVVM is concerned, but if you want to be able to share this functionality across several views, you may create an attached behaviour:
public static class MouseLeftButtonDownBehavior
{
public static ICommand GetCommand(ListView listView) =>
(ICommand)listView.GetValue(CommandProperty);
public static void SetCommand(ListView listView, ICommand value) =>
listView.SetValue(CommandProperty, value);
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(MouseLeftButtonDownBehavior),
new UIPropertyMetadata(null, OnCommandChanged));
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ListView listView = (ListView)d;
ICommand oldCommand = e.OldValue as ICommand;
if (oldCommand != null)
listView.RemoveHandler(UIElement.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown);
ICommand newCommand = e.NewValue as ICommand;
if (newCommand != null)
listView.AddHandler(UIElement.MouseLeftButtonDownEvent, (MouseButtonEventHandler)OnMouseLeftButtonDown, true);
}
private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ListView listView = (ListView)sender;
ICommand command = GetCommand(listView);
if (command != null)
command.Execute(listView.SelectedItem);
}
}
XAML:
<ListView Name="ListViewFiles"
local:MouseLeftButtonDownBehavior.Command="{Binding ListViewItemMouseLeftButtonDownCommand}" />

WPF Drag and Drop multiple listviewitems from listview to a button

I have a listview which property SelectionMode is set to Extended.
Also I have a trash button. I am trying to perform delete operation on selected items on listview when user drags one or multiple selected items (using shift key if more than one item) and drop over delete button.
I have implemented it with a single selected item in listview and it works. Now I am trying to do the same with multiple selected items in listview without success.
Below code works when one single item is selected in listview and user drag and drop it from listview to delete button:
<ListView Margin="10" Name="lvUsers" ItemsSource="{Binding Path=Items}" SelectionMode="Extended">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="MouseMove" Handler="lstItems_MouseMove" />
</Style>
</ListView.ItemContainerStyle>
<!-- other stuff -->
</Listview>
<Button AllowDrop="True" Drop="btnDelete_Drop" Height="64" Width="64" Margin="10" Click="BtnDelete_Click" Content="Delete"/>
and the code-behind:
private void lstItem_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (e.Source != null)
{
DataModel selectedItem = (DataModel)lvUsers.SelectedItem;
DragDrop.DoDragDrop(lvUsers, selectedItem, DragDropEffects.Move);
}
}
}
private void btnDelete_Drop(object sender, DragEventArgs e)
{
Type myType = typeof(DataModel);
string modelns = myType.FullName;
DataModel selectedItem = e.Data.GetData(modelns) as DataModel;
MessageBox.Show(selectedItem.Name);
}
Each listviewitem on listview is of data type below:
public class DataModel
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
public string Mail
{
get;
set;
}
}
As an example, when user drops the dragged listviewitem over delete button I show a message box with the name of the person.
How can I do the same but instead of dragging and dropping one single item from listview, do the same for multiple selected listviewitems? Once dropped, within
btnDelete_Drop I want to iterate over all the listviews items dropped and do some stuff.
I have just implemented a solution that works. I have replaced methods lstItem_MouseMove and btnDelete_Drop in code-behind by these ones:
private void lstItem_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (e.Source != null)
{
List<DataModel> myList = new List<DataModel>();
foreach (DataModel Item in lvUsers.SelectedItems)
{
myList.Add(Item);
}
DataObject dataObject = new DataObject(myList);
DragDrop.DoDragDrop(lvUsers, dataObject, DragDropEffects.Move);
}
}
}
private void btnDelete_Drop(object sender, DragEventArgs e)
{
Type myType = typeof(List<DataModel>);
List<DataModel> selectedItems = e.Data.GetData(myType) as List<DataModel>;
string hello = "Hello ";
foreach (DataModel dm in selectedItems)
{
hello = hello + ", " + dm.Name;
}
MessageBox.Show(hello);
}
This is working but mayber there are any other better solution. Any suggestion will be welcome.

how do i use events of RadDragAndDrop in ViewModel

I am using telrik RadDragAndDrop Tool with the ListBox. I am using silverlight with mvvm light. My question is that how should I use this code in ViewModel. This is a code behind file.
public Construtor()
{
InitializeComponent();
RadDragAndDropManager.AddDragQueryHandler(this, OnDragQuery);
RadDragAndDropManager.AddDragInfoHandler(this, OnDragInfo);
RadDragAndDropManager.AddDropQueryHandler(this, OnDropQuery);
RadDragAndDropManager.AddDropInfoHandler(this, OnDropInfo);
}
private void OnDropInfo(object sender, DragDropEventArgs e)
{
ItemsControl box = e.Options.Destination as ItemsControl;
IList<Section> itemsSource = box.ItemsSource as IList<Section>;
Section section = e.Options.Payload as Section;
if (e.Options.Status == DragStatus.DropComplete)
{
if (!itemsSource.Contains(section))
{
itemsSource.Add(section);
}
}
}
void OnDropQuery(object sender, DragDropQueryEventArgs e)
{
ItemsControl box = e.Options.Destination as ItemsControl;
IList<Section> itemsSource = box.ItemsSource as IList<Section>;
Section section = e.Options.Payload as Section;
e.QueryResult = section != null && !itemsSource.Contains(section);
}
void OnDragInfo(object sender, DragDropEventArgs e)
{
ListBoxItem listBoxItem = e.Options.Source as ListBoxItem;
ListBox box = ItemsControl.ItemsControlFromItemContainer(listBoxItem) as ListBox;
IList<Section> itemsSource = box.ItemsSource as IList<Section>;
Section section = e.Options.Payload as Section;
if (e.Options.Status == DragStatus.DragComplete)
{
if (section != null && itemsSource.Contains(section))
{
itemsSource.Remove(section);
}
}
}
protected virtual void OnDragQuery(object sender, DragDropQueryEventArgs e)
{
ListBoxItem listBoxItem = e.Options.Source as ListBoxItem;
ListBox box = ItemsControl.ItemsControlFromItemContainer(listBoxItem) as ListBox;
if (e.Options.Status == DragStatus.DragQuery && box != null)
{
e.Options.Payload = box.SelectedItem;
ContentControl cue = new ContentControl();
cue.Content = box.SelectedItem;
e.Options.DragCue = cue;
}
e.QueryResult = true;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
SelectingQuestionsWindow window = new SelectingQuestionsWindow();
window.Show();
this.radExpander1.Visibility = System.Windows.Visibility.Visible;
}
<*XAML*>
This is my Xaml.
<ListBox x:Name="SectionListBoxMain" Height="165" Width="200" SelectedItem="{Binding SelectedSectionList}"
DisplayMemberPath="SectionName" ItemsSource="{Binding SectionList}" ItemContainerStyle="{StaticResource draggableItemStyle}">
<telerik:RadDragAndDropManager.AllowDrop>
true
</telerik:RadDragAndDropManager.AllowDrop>
</ListBox>
<TextBlock Width="20" />
<ListBox x:Name="SectionListBox" Height="167" Width="200" ItemsSource="{Binding SelectedSectionList}" telerik:RadDragAndDropManager.AllowDrop="True" DisplayMemberPath="SectionName" ItemContainerStyle="{StaticResource draggableItemStyle}">
</ListBox>
</StackPanel>
This is view logic that does not really belong in your ViewModel. It is probably better suited to a Behavior.
See this example: http://www.telerik.com/help/silverlight/raddraganddrop-within-radgridview.html
They use a Behavior and attach it to a grid to enable row reordering. You could start with something like:
public partial class ListDragDropBehavior : Behavior<ListBox>
You would need to add some dependency properties to bind to the other list box.
You can then use this Behavior on other list boxes by simply attaching it to the list box (i use blend to attach behaviors)

How can i receive a reference to a control in WPF (MVVM)?

In my WPF MVVM Project I have a button that triggers a function that should add a node to a xml and then set the focus to a textbox.
My question is, how can i receive a reference to a control?
View:
<Button Command="{Binding Path=ButtonAddCategory_Click}" />
ViewModel:
RelayCommand buttonAddCategory_Click;
public ICommand ButtonAddCategory_Click
{
get
{
return buttonAddCategory_Click ?? (buttonAddCategory_Click = new RelayCommand(param => this.AddCategory(),
param => true));
}
}
public void AddCategory()
{
...
//get the "node" -> reference?
XmlNode selectedItem = (XmlNode)treeView.SelectedItem;
..
//add the node to the xml
..
//change focus -> reference?
textBoxTitel.Focus();
textBoxTitel.SelectAll();
}
Don't do it in the ViewModel. The ViewModel shouldn't know anything about the view.
You can do it in code-behind:
handle the TreeView.SelectedItemChanged event in code-behind, and update a SelectedItem property on the ViewModel (you could also do it with an attached behavior)
to focus the TextBox, raise an event from the ViewModel and handle it in code-behind:
ViewModel:
public XmlNode SelectedItem { get; set; }
public event EventHandler FocusTitle;
public void AddCategory()
{
...
//get the "node" -> reference?
XmlNode selectedItem = this.SelectedItem;
..
//add the node to the xml
..
// Notify the view to focus the TextBox
if (FocusTitle != null)
FocusTitle(this, EventArgs.Empty);
}
Code-behind:
// ctor
public MyView()
{
InitializeComponent();
DataContextChanged += MyView_DataContextChanged;
}
private void MyView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
MyViewModel vm = (MyViewModel)e.NewValue;
vm.FocusTitle += ViewModel_FocusTitle;
}
private void TreeView1_SelectedItemChanged(object sender, RoutedPropertyChangedEventHandler<Object> e)
{
MyViewModel vm = (MyViewModel)DataContext;
vm.SelectedItem = (XmlNode)e.NewValue;
}
private void ViewModel_FocusTitle(object sender, EventArgs e)
{
textBoxTitle.Focus();
}
You could use the FocusManager.FocusedElement attached property to handle ensuring the TextBox receives focus.
<DataTemplate DataType="{x:Type YourViewModel}">
<Grid FocusManager.FocusedElement="{Binding ElementName=userInput}">
<TextBox x:Name="userInput" />
</Grid>
</DataTemplate>
As for your second part (textBox.SelectAll()) you may have to work on a behavior or attached property of your own that handles the focusing and selecting in one fell swoop.

Clear wpf listbox selection using button in control template and no codebehind

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;
}
}

Resources