Get Values in TextBox inside ListView - wpf

I am created a ListView with TextBox Control.I need to get values in TextBox.
After user typed on Textbox.I need to get whats the user typed.
<Window x:Class="LdiaryEditableListView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Ldiary Editable ListView Sample" Height="350" Width="300">
<StackPanel >
<ListView Name="listView">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="Button.Click" Handler="Button_Click"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=FirstName}" Value="">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Text="{Binding Path=FirstName}"/>
<TextBox Width="100" Text="{Binding Path=LastName}"/>
<Button Width="70" >Add</Button>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="100" Header="First Name" DisplayMemberBinding="{Binding Path=FirstName}"></GridViewColumn>
<GridViewColumn Width="100" Header="Last Name" DisplayMemberBinding="{Binding Path=LastName}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
Iam created a function to get each element in textbox in listview.But it dows not work
foreach(DataRowView itm in lstvQualification.Items)
{
MessageBox.Show(itm[0].ToString());
}

I got solution for above my problem.
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace LdiaryEditableListView
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<Person> people;
public MainWindow()
{
InitializeComponent();
people = new ObservableCollection<Person>(){
new Person{FirstName="", LastName=""},
new Person{FirstName = "Ldiary", LastName="Translations"},
new Person{FirstName = "English", LastName="Japanese"}
};
listView.ItemsSource = people;
}
public class Person : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
}
}
private string _lastname;
public string LastName
{
get
{
return _lastname;
}
set
{
_lastname = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Person newPerson = people[0];
if (string.IsNullOrEmpty(newPerson.FirstName) || string.IsNullOrEmpty(newPerson.LastName))
{
newPerson.FirstName = "";
newPerson.LastName = "";
MessageBox.Show("Please provide both first name and last name!");
}
else
{
Person emptyPerson = new Person()
{
FirstName = "",
LastName = ""
};
people.Insert(0, emptyPerson);
}
listView.ItemsSource = null;
listView.ItemsSource = people;
}
}
}

Related

Enable button when checkbox is checked in WPF datagrid

I have a Datagrid in WPF in which first column has checkbox column and last column has buttons.
Initially, I want to make all the buttons disabled and whenever any checkbox is checked then button of that row should get enabled.
checkbox is unchecked then button should be disabled.
Searched a Lot but could not find anything related to this.
I am not using MVVM.. How to do this on the code behind?
Thanks
This is my Xaml Code and I am simply assigning my itemsource on the code behind
<Grid Grid.Row="2" Grid.ColumnSpan="2" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center" >
<Border BorderThickness="0" Margin="10" CornerRadius="15">
<Border.BitmapEffect>
<DropShadowBitmapEffect />
</Border.BitmapEffect>
<Grid>
<Border x:Name="BDRounded" BorderThickness="0" CornerRadius="15" Background="White"/>
<DataGrid HorizontalAlignment="Left" x:Name="dgrdActors" RowHeight="74" AutoGenerateColumns="False" CanUserAddRows="False"
BorderThickness="1,0,0,0" BorderBrush="#FFD1A251" FontSize="28" Foreground="#DCA566" FontFamily="Helvetica Neue"
CanUserResizeRows="False" AlternatingRowBackground="Linen" AlternationCount="2" Background="#DCA566"
RowHeaderWidth="0" CanUserResizeColumns="False" CanUserSortColumns="False" CanUserReorderColumns="False"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible"
HorizontalGridLinesBrush="#FFD1A251" VerticalGridLinesBrush="#FFD1A251" Height="326"
SelectionMode="Extended" SelectionUnit="FullRow" VirtualizingStackPanel.VirtualizationMode="Standard"
Style="{StaticResource DatagridStyle}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="70" CanUserReorder="False" CanUserResize="False" CanUserSort="False" CellStyle="{StaticResource HitVisibilityCellStyle}" HeaderStyle="{StaticResource HeaderStyle}" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Viewbox Margin="-1">
<!--<CheckBox x:Name="chkboxactors" HorizontalAlignment="Center" VerticalAlignment="Center"
IsChecked="{Binding IsActorChecked, UpdateSourceTrigger=PropertyChanged}"></CheckBox>-->
<CheckBox x:Name="chkboxActors" HorizontalAlignment="Center" VerticalAlignment="Center"></CheckBox>
</Viewbox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Actor Name(s)" Width="300" Binding="{Binding ActorName}" CanUserReorder="False" CellStyle="{StaticResource CellStyle}" CanUserResize="False" CanUserSort="False" HeaderStyle="{StaticResource HeaderStyle}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Role(s)" Width="300" Binding="{Binding Role}" CanUserReorder="False" CellStyle="{StaticResource CellStyle}" CanUserResize="False" CanUserSort="False" HeaderStyle="{StaticResource HeaderStyle}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Width="250" CanUserReorder="False" CanUserResize="False" HeaderStyle="{StaticResource HeaderStyle}" CellStyle="{StaticResource HitVisibilityCellStyle}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnSelectRole" Content="Select Role" Style="{StaticResource DatagridButtonStyle}"></Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=BDRounded}"/>
</DataGrid.OpacityMask>
</DataGrid>
</Grid>
</Border>
</Grid>
Here you go!
There's no need to do the enabling/disabling in the ViewModel, as this can all be done in XAML.
XAML:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="100">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="false"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked}" Value="true">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ViewModel:
public class ViewModel
{
public List<Data> Items { get; private set; }
public ViewModel()
{
Items = new List<Data>
{
new Data(),
new Data(),
new Data()
};
}
}
public class Data : INotifyPropertyChanged
{
private bool _isChecked;
public bool IsChecked
{
get {return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
}
Edit:
Since you've requested a code-behind implementation, here you go. This works by traversing the visual tree based on the current row that the checkbox was clicked from.
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DataGrid AutoGenerateColumns="False" x:Name="MyDataGrid">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Click="CheckBox_Clicked"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="100" x:Name="Button" IsEnabled="false" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
XAML.CS:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyDataGrid.ItemsSource = new List<string>
{
"test",
"test1",
"test2",
"test3"
};
}
private void CheckBox_Clicked(object sender, RoutedEventArgs e)
{
var checkBox = sender as CheckBox;
if (checkBox != null)
{
var associatedRow = VisualTreeHelper.GetParent(checkBox);
while ((associatedRow != null) && (associatedRow.GetType() != typeof(DataGridRow)))
{
associatedRow = VisualTreeHelper.GetParent(associatedRow);
}
var dataGridRow = associatedRow as DataGridRow;
if (dataGridRow != null)
{
var associatedButton = FindChild(dataGridRow, "Button");
if (associatedButton != null)
{
associatedButton.IsEnabled = checkBox.IsChecked.HasValue ? checkBox.IsChecked.Value : false;
}
}
}
}
public static Button FindChild(DependencyObject parent, string childName)
{
if (parent == null) return null;
Button foundChild = null;
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var childType = child is Button;
if (!childType)
{
foundChild = FindChild(child, childName);
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
if (frameworkElement != null && frameworkElement.Name == childName)
{
foundChild = (Button)child;
break;
}
}
else
{
foundChild = (Button)child;
break;
}
}
return foundChild;
}
}
If the datagrid is bound to a collection of objects you own (ideally in this case a Facade of a model), then add a IsSelected property to the object that makes up the collection. You can databind your checkbox to that property.
To enable/disable the button, have the model/facade in the collection implement ICommand. You can then use the CanExecute method to enable/disable the button based on the value of IsSelected.
public class User : ICommand, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public bool IsSelected
{
get
{
return this.isSelected;
}
set
{
this.isSelected = value;
CommandManager.InvalidateRequerySuggested();
this.OnPropertyChanged("IsSelected");
}
}
public bool CanExecute(object parameter)
{
return this.IsSelected;
}
public void Execute(object parameter)
{
// ... Do stuff ...
}
private void RaiseCanExecuteChanged()
{
var handler = this.CanExecuteChanged;
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(property));
}
private void OnPropertyChanged(string property)
{
var handler = this.PropertyChanged;
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(property));
}
}
Now you bind your checkbox to the IsSelected property. Anytime that the checkbox is selected, the CanExecute method will fire on the class.
Ideally you would use a DelegateCommand class from either MVVMLight or Prism, which have a RaiseCanExecuteChanged() method. This lets you avoid using the CommandManager to requery it.
This can be your DataGrid definition:
<DataGrid x:Name="TestDataGrid" ItemsSource="{Binding source}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="TestBox" Content="Test" IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Click" IsEnabled="{Binding IsChecked}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Pretty simple code behind:
public partial class MainWindow : Window
{
public ObservableCollection<Model> source { get; set; }
public MainWindow()
{
InitializeComponent();
source = new ObservableCollection<Model>();
source.Add(new Model());
source.Add(new Model());
this.DataContext = this;
}
}
This could be your model:
public class Model : DependencyObject
{
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(Model), new PropertyMetadata(false));
}
Or implement INPC inteface:
public class Model : INotifyPropertyChanged
{
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; }
set
{
_IsChecked = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
< DataGridTemplateColumn Header="{ Loc CellSettings_Min}" >
<DataGridTemplateColumn.CellTemplate >
< DataTemplate >
< TextBox Width="100" Text="{ Binding Interval }" >
<TextBox.Style>
< Style TargetType = "TextBox" >
< Setter Property="IsEnabled" Value="false" />
< Style.Triggers >
< DataTrigger Binding = "{ Binding AutoScale }" Value= "True" >
< Setter Property = "IsEnabled" Value = "False" />
< /DataTrigger >
< DataTrigger Binding = "{ Binding AutoScale }" Value="False" >
< Setter Property = "IsEnabled" Value = "True" />
</ DataTrigger >
</ Style.Triggers >
</ Style >
</ TextBox.Style >
</ TextBox >
</ DataTemplate >
</ DataGridTemplateColumn.CellTemplate >
</ DataGridTemplateColumn >
You should use MVVM.DataBinding is convenient .
Xaml
<CheckBox Name="checkbox" IsChecked="{Binding Checked, Mode = TwoWay}" />
<Button IsEnabled="{Binding ButtonEnabled , Mode = TwoWay}" />
C#
In ViewModel
public class ViewMode : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _buttonChecked = false;
public bool ButtonChecked
{
get
{
return _buttonChecked;
}
set
{
if(value == true)
{
_buttonChecked = value;
OnPropertyChanged("ButtonChecked");
}
}
}
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
if(value == true)
{
_checked= value;
ButtonChecked = value;
OnPropertyChanged("Checked");
}
}
}
[NotifyPropertyChangedInvocator]
private virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

WPF: how to auto update ObservableCollection inside ListView

I have my object collection:
public class Data
{
string name {get; set;}
int progres {get; set;}
}
public ObservableCollection<Data> dataFiles { get; set; }
And my ListView:
<ListView Name="lvDataFiles"
ItemsSource="{Binding dataList}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Foreground" Value="White"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<DataTemplate x:Key="MyDataTemplate">
<Grid Margin="-6">
<ProgressBar Name="prog" Maximum="100" Value="{Binding Progress}"
Width="{Binding Path=Width, ElementName=ProgressCell}"
Height="16" Margin="0" Foreground="#FF5591E8" Background="#FF878889" />
<TextBlock Text="{Binding Path=Value, ElementName=prog, StringFormat={}{0}%}" VerticalAlignment="Center"
HorizontalAlignment="Center" FontSize="11" Foreground="White" />
</Grid>
</DataTemplate>
<ControlTemplate x:Key="ProgressBarTemplate">
<Label HorizontalAlignment="Center" VerticalAlignment="Center" />
</ControlTemplate>
</ListView.Resources>
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource ListViewHeaderStyle}">
<GridViewColumn Width="425" Header="File name" DisplayMemberBinding="{Binding FileName}" />
<GridViewColumn x:Name="ProgressCell" Width="50" Header="Progress"
CellTemplate="{StaticResource MyDataTemplate}" />
</GridView>
</ListView.View>
</ListView>
My ListView has 2 columns: file name and Progress (that contain progress bar)
My Data collection has the Progress property that changing every few seconds.
Is it possible that my ListView ProgressBar will update automatic each time specific object (or several in the same time..) changing ?
Or i need to go over my collection and update ?
Your Data class must inherit from INotifyPropertyChanged, add a NotifyPropertyChange method and call that for each setter.
public class Data : INotifyPropertyChanged
{
private string _name;
public string name
{
get { return _name; }
set
{
_name= value;
NotifyPropertyChanged("name");
}
}
private int _progress;
public int progress
{
get { return _progress; }
set
{
_progress = value;
NotifyPropertyChanged("progress");
}
}
public event PropertyChangedEventHandler PropertyChanged;
virtual public void NotifyPropertyChange( string propertyName )
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}

How to Scroll to a new item added to a list view in MVVM Light

I've got a project where there is a ListView and when the User clicks the New button the new Object is added to the bottom of the ListView. I've tried using a Content Style class but that didn't work. I just need something that will scroll to the selected item. Below is my code:
View:
<ListView Margin="103,0,0,10" ScrollViewer.CanContentScroll="True" Height="87" SelectedItem="{Binding SelectedSession, Mode=TwoWay}" ItemsSource="{Binding SessionCollection}">
<ListView.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Session Name" Width="180" DisplayMemberBinding="{Binding SessionName, Mode=TwoWay}" />
<GridViewColumn Header="Operator Name" Width="180" DisplayMemberBinding="{Binding OperatorName, Mode=TwoWay}"/>
<GridViewColumn Header="Session Date" Width="180" DisplayMemberBinding="{Binding SessionDate, Mode=TwoWay}"/>
</GridView>
</ListView.View>
</ListView>
View Model code for the New Button :
public void NewSession()
{
Session newSession = new Session();
SessionCollection.Add(newSession);
SelectedSession = newSession;
SessionDate = DateTime.Now;
StartTime = DateTime.Now;
EndTime = DateTime.Now;
ProjectManager.Instance.CurrentSession = null;
}
public ObservableCollection<Session> SessionCollection
{
get
{
if (currentDatabaseProj.Sessions == null)
{
return currentDatabaseProj.Sessions;
}
else
{
return currentDatabaseProj.Sessions;
}
}
private set
{
currentDatabaseProj.Sessions = value;
IsNewProjClicked = true;
this.RaisePropertyChanged("SessionCollection");
}
}
One simple handler in the code behind should do the trick
(i simplify your code to made it clear)
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView x:Name="listView" Margin="10" ScrollViewer.CanContentScroll="True" SelectedItem="{Binding SelectedSession, Mode=TwoWay}" ItemsSource="{Binding SessionCollection}">
<ListView.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Session Name" Width="180" DisplayMemberBinding="{Binding}" />
</GridView>
</ListView.View>
</ListView>
<Button Click="UIElement_NewElementHandler" Grid.Row="1" Content="Add Item" Command="{Binding AddItem}"></Button>
</Grid>
and in the view's code behind add the following handler :
private void UIElement_NewElementHandler(object sender, RoutedEventArgs routedEventArgs)
{
var border = VisualTreeHelper.GetChild(listView, 0) as Decorator;
var scrollViewer = border.Child as ScrollViewer;
scrollViewer.ScrollToBottom();
}
ps: scrolling down to a new added item is something ViewRelated, none of the View Model Properties are used in the codebehind, so i believe you are not violating any mvvm rule by doing that.
Working example with behavior
xaml
<Window x:Class="ListView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:listView="clr-namespace:ListView"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Content="Add New" Grid.Row="0" Click="Button_Click"></Button>
<ListView Grid.Row="1" ItemsSource="{Binding MySessionList}" SelectedItem="{Binding SelectedSession, Mode=TwoWay}">
<i:Interaction.Behaviors>
<listView:ScrollIntoViewBehavior/>
</i:Interaction.Behaviors>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="200" DisplayMemberBinding="{Binding Name, Mode=TwoWay}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
cs
public partial class MainWindow : Window
{
private Viewmodel _data;
public MainWindow()
{
InitializeComponent();
_data = new Viewmodel();
this.DataContext = _data;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_data.AddNew();
}
}
public class Viewmodel : INotifyPropertyChanged
{
private MySession _selectedSession;
public ObservableCollection<MySession> MySessionList { get; set; }
public Viewmodel()
{
this.MySessionList = new ObservableCollection<MySession>();
//fill with some data
for (int i = 0; i < 20; i++)
{
this.MySessionList.Add(new MySession(){Name = "Index : " + i});
}
}
public MySession SelectedSession
{
get { return _selectedSession; }
set
{
_selectedSession = value;
OnPropertyChanged();
}
}
//should be a Command for MVVM but for quick and dirty
public void AddNew()
{
var toAdd = new MySession() {Name = "New Added " + DateTime.Now.ToLongTimeString()};
this.MySessionList.Add(toAdd);
this.SelectedSession = toAdd;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MySession
{
public string Name { get; set; }
}
//ADD REFERENCE: System.Windows.Interactivity
public class ScrollIntoViewBehavior : Behavior<ListBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
private void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var ctrl = sender as ListBox;
if (ctrl == null)
return;
if (ctrl.SelectedItem != null)
{
ctrl.Dispatcher.BeginInvoke(
DispatcherPriority.Render,
new Action(() =>
{
ctrl.UpdateLayout();
ctrl.ScrollIntoView(ctrl.SelectedItem);
}));
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
}
}

WPF Dynamic menu associated with the object?

I'd like to be able to create dynamic menus associated with certain object. Let’s say, I will have 3 listview container with one style where I also have a Menu. I need to generate different menu items from collection of the RoutetUICommands in relation on each listview. I was trying to solve this puzzle but took me a while and still have trouble making it work. I need to generate object specific menus, an unique menu for each listview. Any ideas are highly appreciated. Thank you!
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
x:Class="DynamicMenu.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<Style x:Key="ListViewStyleTask" TargetType="{x:Type ListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Grid>
<Menu x:Name="mainMenu">
<MenuItem x:Name="menuItem" Header="Tasks" ItemsSource="{Binding Commands}" >
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding}" />
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ListView x:Name="Container1" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<ListView x:Name="Container2" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<ListView x:Name="Container3" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
</Grid>
I have added some commands which I need to associate to 3 different Listviews:
public partial class MainWindow : Window
{
// Container 1
public static RoutedUICommand NameCommand = new RoutedUICommand("Name", "NameCommand", typeof(MainWindow));
public static RoutedUICommand StreetCommand = new RoutedUICommand("Street", "StreetCommand", typeof(MainWindow));
public static RoutedUICommand GroupCommand = new RoutedUICommand("Add to Group", "AddGroup", typeof(MainWindow));
// Container 2
public static RoutedUICommand ViewDetailsCommand = new RoutedUICommand("View Details", "ViewDetailsCommand", typeof(MainWindow));
// Container 3
public static RoutedUICommand StartCommand = new RoutedUICommand("Start", "StartCommand", typeof(MainWindow));
public static RoutedUICommand StopCommand = new RoutedUICommand("Stop", "StopCommand", typeof(MainWindow));
public static RoutedUICommand LoadCommand = new RoutedUICommand("Load", "LoadCommand", typeof(MainWindow));
public MainWindow()
{
this.InitializeComponent();
// Insert code required on object creation below this point.
}
}
}
You need to define a structure to group the data as you need in your control template. Something like this,
public class CommandCollection {
public ObservableCollection<Command> Commands { get; set; }
}
public class Command {
public ICommand Action { get; set; }
public string Text { get; set; }
public string Parameter { get; set; }
}
Have 3 members of CommandCollection each one having its commands and then assign those as datacontext to the ListViews
Updated,
After declaring the above structure you declare 3 members,
public CommandCollection Container1Commands { get; set; }
public CommandCollection Container2Commands { get; set; }
public CommandCollection Container3Commands { get; set; }
Fill these members,
Container1Commands = new CommandCollection ();
Container1Commands.Commands = new ObservableCollection<CommandParameters> ();
Container1Commands.Commands.Add (new CommandParameters () { Action = NameCommand, Text = "Name" });
Container1Commands.Commands.Add (new CommandParameters () { Action = StreetCommand, Text = "Street" });
Container1Commands.Commands.Add (new CommandParameters () { Action = GroupCommand, Text = "Group" });
Container2Commands = new CommandCollection ();
Container2Commands.Commands = new ObservableCollection<CommandParameters> ();
Container2Commands.Commands.Add (new CommandParameters () { Action = ViewDetailsCommand, Text = "ViewDetails" });
Container3Commands = new CommandCollection ();
Container3Commands.Commands = new ObservableCollection<CommandParameters> ();
Container3Commands.Commands.Add (new CommandParameters () { Action = StartCommand, Text = "Start" });
Container3Commands.Commands.Add (new CommandParameters () { Action = StopCommand, Text = "Stop" });
Container3Commands.Commands.Add (new CommandParameters () { Action = LoadCommand, Text = "Load" });
Set data context,
this.DataContext = this;
this.Container1.DataContext = Container1Commands;
this.Container2.DataContext = Container2Commands;
this.Container3.DataContext = Container3Commands;
Update your control template to specify menu item command binding,
<Setter Property="Command" Value="{Binding Action}" />
Updated
XAML
<Window.Resources>
<Style x:Key="ListViewStyleTask" TargetType="{x:Type ListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Grid>
<Menu x:Name="mainMenu" >
<MenuItem x:Name="menuItem" Header="Tasks" ItemsSource="{Binding Commands}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Action}" />
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListView
Grid.Row="0" x:Name="Container1" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<ListView
Grid.Row="1" x:Name="Container2" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<ListView
Grid.Row="2" x:Name="Container3" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
</Grid>
Code behind
// Container 1
public static RoutedUICommand NameCommand = new RoutedUICommand ("Name", "NameCommand", typeof (Window1));
public static RoutedUICommand StreetCommand = new RoutedUICommand ("Street", "StreetCommand", typeof (Window1));
public static RoutedUICommand GroupCommand = new RoutedUICommand ("Add to Group", "AddGroup", typeof (Window1));
// Container 2
public static RoutedUICommand ViewDetailsCommand = new RoutedUICommand ("View Details", "ViewDetailsCommand", typeof (Window1));
// Container 3
public static RoutedUICommand StartCommand = new RoutedUICommand ("Start", "StartCommand", typeof (Window1));
public static RoutedUICommand StopCommand = new RoutedUICommand ("Stop", "StopCommand", typeof (Window1));
public static RoutedUICommand LoadCommand = new RoutedUICommand ("Load", "LoadCommand", typeof (Window1));
public Window1 () {
InitializeComponent ();
this.Loaded += new RoutedEventHandler (Window1_Loaded);
}
public CommandCollection Container1Commands { get; set; }
public CommandCollection Container2Commands { get; set; }
public CommandCollection Container3Commands { get; set; }
void Window1_Loaded (object sender, RoutedEventArgs e) {
Container1Commands = new CommandCollection ();
Container1Commands.Commands = new ObservableCollection<Command> ();
Container1Commands.Commands.Add (new Command () { Action = NameCommand, Text = "Name" });
Container1Commands.Commands.Add (new Command () { Action = StreetCommand, Text = "Street" });
Container1Commands.Commands.Add (new Command () { Action = GroupCommand, Text = "Group" });
Container2Commands = new CommandCollection ();
Container2Commands.Commands = new ObservableCollection<Command> ();
Container2Commands.Commands.Add (new Command () { Action = ViewDetailsCommand, Text = "ViewDetails" });
Container3Commands = new CommandCollection ();
Container3Commands.Commands = new ObservableCollection<Command> ();
Container3Commands.Commands.Add (new Command () { Action = StartCommand, Text = "Start" });
Container3Commands.Commands.Add (new Command () { Action = StopCommand, Text = "Stop" });
Container3Commands.Commands.Add (new Command () { Action = LoadCommand, Text = "Load" });
this.CommandBindings.Add (new CommandBinding (NameCommand, ExecuteNameCommand, CanExecuteNameCommand));
this.CommandBindings.Add (new CommandBinding (StreetCommand, ExecuteStreetCommand, CanExecuteStreetCommand));
this.CommandBindings.Add (new CommandBinding (GroupCommand, ExecuteGroupCommand, CanExecuteGroupCommand));
this.DataContext = this;
this.Container1.DataContext = Container1Commands;
this.Container2.DataContext = Container2Commands;
this.Container3.DataContext = Container3Commands;
}
private void ExecuteNameCommand (object inSender, RoutedEventArgs inE) {
MessageBox.Show ("Name command Executed");
}
private void CanExecuteNameCommand (object inSender, CanExecuteRoutedEventArgs inE) {
inE.CanExecute = true;
}
private void ExecuteStreetCommand (object inSender, RoutedEventArgs inE) {
MessageBox.Show ("Street command Executed");
}
private void CanExecuteStreetCommand (object inSender, CanExecuteRoutedEventArgs inE) {
inE.CanExecute = true;
}
private void ExecuteGroupCommand (object inSender, RoutedEventArgs inE) {
MessageBox.Show ("Group command Executed");
}
private void CanExecuteGroupCommand (object inSender, CanExecuteRoutedEventArgs inE) {
inE.CanExecute = true;
}
Other classes
public class CommandCollection {
public ObservableCollection<Command> Commands { get; set; }
}
public class Command {
public ICommand Action { get; set; }
public string Text { get; set; }
public string Parameter { get; set; }
}
I Hope now you get it working.
Updated for RoutedUICommand description,
The idea should be to have these menu items in the outer container (like shell) which will have other pages in it (like a frame/canvas), like for example if you see MS Visual Studio the menu items (Save) are part of the application shell and the files openeed are within the shell (shell has a container tabcontrol maybe, where the files are loaded as they are opened). So the routed commands (Save) are defined by the application shell and all the other pages inside the shell's container add those commands in there command binding collection (this.CommandBindings.Add(cmdname, actionname, predicatename)) so each page performs its own respective action and the command is invoked for them only when they are in focus.

WPF DataGrid ComboBox causes InvalidOperationException

I am getting an InvalidOperationException('DeferRefresh' is not allowed during an AddNew or EditItem transaction.) from my datagrid when I try to edit the value of a combo box column. The items I am showing all have a reference to one other item in the same list so this is what I am using the combobox for. It is bound to the same collection as the datagrid is. My application I am working on is targetting .NET 3.5, but I have put together an example that is exactly the same in .NET 4 since the datagrid is built in. Here is the code for items in the datagrid:
public class TestItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private int m_ID;
private string m_Name;
private int m_OppositeID;
public int ID
{
get { return m_ID; }
set
{
m_ID = value;
RaisePropertyChanged("ID");
}
}
public string Name
{
get { return m_Name; }
set
{
m_Name = value;
RaisePropertyChanged("Name");
}
}
public int OppositeID
{
get { return m_OppositeID; }
set
{
m_OppositeID = value;
RaisePropertyChanged("OppositeID");
}
}
public TestItem(int id, string name, int oppID)
{
ID = id;
Name = name;
OppositeID = oppID;
}
}
This is the code in my window:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private ObservableCollection<TestItem> m_Items;
public ObservableCollection<TestItem> Items
{
get { return m_Items; }
set
{
m_Items = value;
RaisePropertyChanged("Items");
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Items = new ObservableCollection<TestItem>();
Items.Add(new TestItem(0, "Fixed", 0));
Items.Add(new TestItem(1, "Left Side", 2));
Items.Add(new TestItem(2, "Right Side", 1));
}
}
and finally my xaml:
<Window x:Class="DataGrid_Combo_Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False">
<DataGrid.Resources>
<Style x:Key="ItemsSourceStyle" TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.Items, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=ID}" Header="ID" Width="*"/>
<DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" Width="*"/>
<DataGridComboBoxColumn Header="Opposite Item" Width="*" DisplayMemberPath="Name" SelectedValuePath="ID" SelectedValueBinding="{Binding Path=OppositeID}" ElementStyle="{StaticResource ItemsSourceStyle}" EditingElementStyle="{StaticResource ItemsSourceStyle}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Thanks in advance for any assistance you can offer!
I found out how to fix this issue.
I created a CollectionViewSource like this in my Window.Resources:
<Window.Resources>
<CollectionViewSource x:Key="itemSource" Source="{Binding Path=Items}"/>
</Window.Resources>
Then changed my combobox column definition to the following:
<DataGridComboBoxColumn Header="Opposite Item" Width="*" DisplayMemberPath="Name" SelectedValuePath="ID" SelectedValueBinding="{Binding Path=OppositeID}" ItemsSource="{Binding Source={StaticResource itemSource}}"/>
Try following sequence before calling Refersh on CollectionView or DataGridXYZ.Items
DataGridX.CommitEdit();
DataGridX.CancelEdit();
Worked for me.

Resources