WPF - Extend ListView with checkable AND selectable ListViewItems - wpf

I already read many examples on extending ListViews with checkboxes bound with IsSelected. But I want something more.
I want a seperation between the checked and selected state, so i get a ListBox that has a single selected item, but can have multiple checked items.
Unfortunately ListViewItem does not have a property for checked and I dont see a possibility to get the ListView to work with a custom CheckableListViewItem.
Of course i could use a List of objects with a checked property as ItemSource, but I dont think thats a good way to go. Checked or not is a matter of the list or item-container, not of the object listed in it. Beside that I dont want all my classes like user, role, group to have counterparts like checkableUser, checkableRole and checkableGroup.
The behaviour i want can be easyly accomblished for the UI with a
<DataTemplate x:Key="CheckBoxCell">
<StackPanel Orientation="Horizontal">
<CheckBox />
</StackPanel>
</DataTemplate>
and a
<GridViewColumn CellTemplate="{StaticResource CheckBoxCell}" Width="30"/>
But without a binding on the checkbox i cant check if it is checked or not.
Is there any way to accomplish something like that? The perfect solution for me would be to have listView1.SelectedItem, listView1.CheckedItems and maybe a listView1.UncheckedItems and of course listView1.CheckItem and listView1.UncheckItem.
Thanks for any help.

Ok, I got it.
Wasn't much to do, but becouse I'm new to the whole WPF stuff, its been some work to figure out.
Here is the solution:
public class CheckableListViewItem : ListViewItem
{
[Category("Appearance")]
[Bindable(true)]
public bool IsChecked { get; set; }
}
public class CheckableListView : ListView
{
public IList CheckedItems
{
get
{
List<object> CheckedItems = new List<object>();
for (int i=0;i < this.Items.Count; ++i)
{
if ((this.ItemContainerGenerator.ContainerFromIndex(i) as CheckableListViewItem).IsChecked)
CheckedItems.Add(this.Items[i]);
}
return CheckedItems;
}
}
public bool IsChecked(int index)
{
if (index < this.Items.Count) return (this.ItemContainerGenerator.ContainerFromIndex(index) as CheckableListViewItem).IsChecked;
else throw new IndexOutOfRangeException();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
if (item is CheckableListViewItem) return true;
else return false;
}
protected override DependencyObject GetContainerForItemOverride()
{
return new CheckableListViewItem();
}
}
Insert into your XAML under Window.Resources (clr = my class namespace):
<DataTemplate x:Key="CheckBoxCell">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsChecked,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type clr:CheckableListViewItem}}}" />
</StackPanel>
</DataTemplate>
And this as your CheckableListView:
<clr:CheckableListView SelectionMode="Single" [...] >
<ListView.View>
<GridView>
<GridViewColumn CellTemplate="{StaticResource CheckBoxCell}"
Width="30"/>
[...]
</GridView>
</ListView.View>
</clr:CheckableListView>
Maybe this helps someone with the same problem.

In order to do this, you will have to create a custom ListBox and custom ListBoxItem controls to use within your application. Otherwise, you would have to add it to the items in your lists as a generic object ICheckable<T> (where T is User or Role) and have an ICheckableCollection<ICheckable<T>> for your items instead of adding a checkable to your model objects.

Related

How to bind an ItemsControl to a dependency property of a usercontrol?

Trying to understand this better.
I have an ItemsControl defined in my mainview something like this
<ItemsControl Grid.Column="0" Grid.Row="2"
ItemsSource="{Binding Notes}"
ItemTemplate="{Binding Source={StaticResource MyParagraph}}"
>
</ItemsControl>
in which I would like to use a DataTemplate:
<UserControl.Resources>
<DataTemplate x:Key="MyParagraph">
<v:InkRichTextView
RichText="{Binding ?????? "
</DataTemplate>
</UserControl.Resources>
The InkRichTextView is a view with a dependency property, RichText, being used to pass a paragraph from the ObservableCollection(InkRichViewModel) Notes in the mainview to the user control. That is, this works correctly for one paragragh:
<v:InkRichTextView RichText ="{Binding Path=Note}" Grid.Column="0" Grid.Row="0" />
where Note is defined as a paragraph in the MainView.
The problem is, how do I write the DataTemplate and the ItemsControl such that the ItemsControl can pass each paragraph from the observablecollection to the dependency property RichText in the InkRichTextView?
Thanks for any guidance.
(I hope this is understandable!)
Items control:
<ItemsControl x:Name="NotesItemsControl" Grid.Column="2" HorizontalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<local:InkRichTextView RichText="{Binding Note}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Code behind:
class InkRichViewModel : System.ComponentModel.INotifyPropertyChanged
{
#region Note (INotifyPropertyChanged Property)
private string _note;
public string Note
{
get { return _note; }
set
{
if (_note != value)
{
_note = value;
RaisePropertyChanged("Note");
}
}
}
#endregion
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string p)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(p));
}
}
}
public MainWindow()
{
InitializeComponent();
var item01 = new InkRichViewModel() { Note = "Note 01", };
var item02 = new InkRichViewModel() { Note = "Note 02", };
var item03 = new InkRichViewModel() { Note = "Note 03", };
var item04 = new InkRichViewModel() { Note = "Note 04", };
var item05 = new InkRichViewModel() { Note = "Note 05", };
var itemList = new List<InkRichViewModel>()
{
item01, item02, item03, item04, item05,
};
NotesItemsControl.ItemsSource = itemList;
}
How it looks at runtime:
Is that what you're looking for?
Based on what you describe, it seems that each item in your ItemsControl is a paragraph, the very object you want to assign to the InkRichTextView.RichText property. Is that correct?
If so, keep in mind that within the item template, the data context is the collection item itself - thus, the path you are looking for does not refer to a property of the data context, but to the data context itself.
That is done with the dot (.) path:
<v:InkRichTextView RichText="{Binding .}"/>
I'm posting this as an answer, although the credit goes to O.R.Mapper and Murven for pointing me in the right direction. My post is to help anyone else just learning this.
In very simple terms, the ItemControl performs a looping action over the collection in its ItemsSource. In my case the ItemsSource is a collection of type InkRichViewModel. (Hence the question from Murven). In its looping action, the ItemsSource will create objects from the InkRichViewModel. (Thus, my usercontrol now has an individual datacontext!) Each of these objects will use the ItemTemplate for display. So to simplify things, I moved the DataTemplate from the UserControl Resources to within the ItemControl itself as:
<ItemsControl Grid.Column="0" Grid.Row="2"
ItemsSource="{Binding Notes}"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<v:InkRichTextView RichText="{Binding Note}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now that each of my usercontrols has its own datacontext being assigned by the ItemsControl, the Output window (VS2010) now shows the binding errors. Fixing these errors leads to a working solution.
Hope this helps other newbies like myself. Thanks everyone.
(Ooops! Just saw the answer from Murven but I'll leave this if it helps somebody to understand.)

WPF ListView ignores SelectedItem-change

I am working with a ListView in WPF which is bound an observable collection. I then bound the SelectedItem-Property to a property of the ViewModel.
When I select an item in the ListView via GUI "SelectedItem" is changed.
When I change "SelectedItem" in the ViewModel, the ListView only changes, when I set the SelectedItem to NULL.
When I set any other (valid!) object (like the first entry of the ObservableCollection) the ListView just ignores it.
Furthermore: When I want to "Veto" a SelectedItem Change (because data is not saved), the ListView highlights the new selected item instead of the SelectedItem-Property of the ViewModel.
I already tried to change the Binding to Mode=TwoWay - doesn't work as well (otherwise the "NULL" change to SelectedItem wouldn't work as well)
This is the code from the view:
<ListView ItemsSource="{Binding Configurations}" SelectedItem="{Binding SelectedUserConfiguration}" SelectionMode="Single">
<ListView.View>
<GridView>
<GridViewColumn Header="User Configuration" DisplayMemberBinding="{Binding ConfigurationName}" Width="200" />
</GridView>
</ListView.View>
</ListView>
And my ViewModel:
public ObservableCollection<UserConfigurationViewModel> Configurations { get; private set; }
private UserConfigurationViewModel _selectedUserConfiguration;
public UserConfigurationViewModel SelectedUserConfiguration
{
get {
return this._selectedUserConfiguration;
}
set
{
if (this._selectedUserConfiguration != null && this._selectedUserConfiguration.WasChanged)
{
if (ask-user)
{
this._selectedUserConfiguration.Reset();
this._selectedUserConfiguration = value;
}
}
else
{
this._selectedUserConfiguration = value;
}
NotifyOfPropertyChange(() => this.SelectedUserConfiguration);
}
}
In order to select the selected item in any collection control from code, the selected item must be an actual item from the collection that is bound to the ItemsSource property. This can be achieved easily enough with LinQ if your collection items have at least one unique property:
SelectedUserConfiguration = Configurations.Where(c => c.UniqueProperty ==
valueOfItemToSelect).FirstOrDefault();
If your data type objects do not have a unique property, you can simply add an int Id property for this purpose.
I was just having the same issue with a TabControl.
I've tried to create a int property and bind it to SelectedIndex, but no success.
Strangely enough this did the trick:
In your ListView set IsSynchronizedWithCurrentItem="True"
Set a ListView.Resources like this:
<ListView.Resources>
<Style TargetType="ListViewItem" x:Key="ListViewTemplate">
<Setter Property="IsSelected" Value="True" />
</Style>
</ListView.Resources>

How to select all checkbox inside telerik combox on checbox Checked event?

I have on checkbox inside telerik combo control. If User click on "All" option from checkbox list then I want select all checkboxs.
checkbox values.
My Sample code is below.
<telerik:RadComboBox Name="rcbDays" Grid.Row="1" Grid.Column="1" Width="200" HorizontalAlignment="Left" ItemsSource="{Binding MonthDaysList}" VerticalAlignment="Center" >
<telerik:RadComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="chkDays" Content="{Binding DaysText}"
Tag="{Binding DaysValue}" Checked="chkDays_Checked" />
</StackPanel>
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
private void chkWeeks_Checked(object sender, RoutedEventArgs e)
{
//Here I want code for selecting all checkboxes.
}
The items that you bound the ComboBox to should have a property like IsSelected, then you should bind IsChecked of the data-template CheckBox to that. Then you just need to iterate over the source collection and set IsSelected=true on all items.
e.g.
public class MyClass : MyBaseClass // Whatever you may have called it,
{
public bool IsSelected { ... }
public string DaysText { ... }
//...
}
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Content="{Binding DaysText}" Tag="{Binding DaysValue}" />
</StackPanel>
</DataTemplate>
//In the handler that is supposed to select all
foreach (var item in MonthDaysList) item.IsSelected = true;
Of course the property needs to have change notifications.
(Also a note on usability: I do not thing that ComboBoxes should contain CheckBoxes, if you need multiple item selection use a ListBox)
You need to take one more property isSelected as said by H.B.
add IsChecked="{Binding IsSelected}" to CheckBox tag in xaml file. Create one property in the appropriate class i.e. public bool isSeleted.......
When you get in to event chkWeeks_Checked() in this function get reference of the ComboBox item source like objList = (TypeCastYourClassType)YourComboBox.ItemSource;... Now the objList contains all checkbox items. Iterate through objList collection and get isSeleted property for each and every single item and that's done....
In your case
MonthDayList = (TypeCastYourClassType)rcbDays.ItemSource;
for(int i=0;i<MonthDayList.Count;i++)
{
MonthDayList[i].isSelected = true;
}
Here is some good discussion for allowing multiple values to be selected in the telerik combobox.
It uses checkbox within combobox
http://codedotnets.blogspot.in/2012/02/checkboxes-in-comboxes-to-allow.html
Thanks :)

DatagridComboBoxColumn.ItemsSource does not reflect changes from source other than the source bound for datagrid

ComboBox items do not reflect changes made from its source
Here is what I am trying to accomplish:
I have a WPF datagrid that binding to a database table, inside the datagrid there is a combobox(group ID) column bind to one of the columns from the database table; the combobox items are from another table(a list of group ID). The problem now is when the groupd ID list is changed from other table, the combo box items does not take effect.
Can anyone help? Have been stuct for a long time.
Here is XAML code:
<DataGridTemplateColumn Header="Group ID">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding GroupID, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Name="ComboBoxTeamGrpID" SelectedItem="{Binding GroupID, Mode=TwoWay}" ItemsSource="{StaticResource ResourceKey=GroupIDList}">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Here is the code for GroupIDList:
public class GroupIDList : List<string>
{
public GroupIDList()
{
try
{
string tmp = ConfigurationManager.AppSettings["DataSvcAddress"];
Uri svcUri = new Uri(tmp);
JP790DBEntities context = new JP790DBEntities(svcUri);
var deviceQry = from o in context.Devices
where o.GroupID == true
select o;
DataServiceCollection<Device> cList = new DataServiceCollection<Device>(deviceQry);
for (int i = 0; i < cList.Count; i++)
{
this.Add(cList[i].ExtensionID.Trim());
}
this.Add("None");
//this.Add("1002");
//this.Add("1111");
//this.Add("2233");
//this.Add("5544");
}
catch (Exception ex)
{
string str = ex.Message;
}
}
}
Here is another problem related, can anyone help? thank you.
It is either because your GroupIdList is a List and not an ObservableCollection, or because you're binding to a StaticResource, which WPF assumes is unchanged so is only loaded once.
Change your List<string> to an ObservableCollection<string> which will automatically notify the UI when it's collection gets changed, and if that still doesn't work than change your ItemsSource from a StaticResource to a RelativeSource binding, such as
ItemsSource="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=DataContext.GroupIdList}"
Edit
Your parent ViewModel which has your DataGrid's ItemsSource collection should look something like below. Simply add another public property for GroupIdList and have it return your list. Then use the above RelativeSource binding to access it, assuming your DataGrid's ItemsSource is bound in the form of <DataGrid ItemsSource="{Binding MyDataGridItemsSource}" ... />
public class MyViewModel
{
private ObservableCollection<MyDataObject> _myDataGridItemsSource;
public ObservableCollection<MyDataObject> MyDataGridItemsSource
{
get { return _myDataGridItemsSource; }
set
{
if (value != _myDataGridItemsSource)
{
_myObjects = value;
ReportPropertyChanged("MyDataGridItemsSource");
}
}
}
private ObservableCollection<string> _groupIdList = new GroupIdList();
public ObservableCollection<string> GroupIdList
{
get { return _groupIdList; }
}
}
WPF will not poll everytime and check if your list changed. In Order to do this, as Rachel pointed at you should do something like :
public class GroupIDList : ObseravableCollection<string>
EDIT :
Here is my suggestion :
I actually wouldn't do it the way you did. What I do is I create a View Model for the whole grid, that looks like :
public class MyGridViewModel : DependencyObject
Which I would use as data context for my grid:
DataContext = new MyGridViewModel ();
Now the implementation of MyGridViewModel will contain a list of ViewModel that represent my GridRows, which is an ObservableCollection
public ObservableCollection<RowGridViewModel> RowItemCollection { get; private set; }
I will this in my dataGrid as such :
<Grid>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding RowItemCollection}" SelectionMode="Extended" SelectionUnit="Cell">
<DataGrid.Columns>
and All you need to do, is to fill in you RowItemColleciton with the correct data, and then bind you Columns to the correct Property in RowGridViewModel...in your case it would look like (but you have to initialize the GroupIDList :
public class RowGridViewModel: DependencyObject
{
public List<String> GroudIDList { get; set;
}
}
Let me if that help

Silverlight + ItemsControl + Get Control Property Value

I have an ItemsControl with a DataTemplate that has been defined. My ItemsControl definition looks like the following:
<ItemsControl x:Name="myItemsControl" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<CheckBox x:Name="myCheckBox" Content="{Binding Name}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is a simplified version of my DataTemplate. Regardless, when a user clicks a button on the page, I want to loop through the items in myItemsControl and determine if the CheckBox element associated with the item is checked.
How do I determine if a CheckBox is checked for a specific item within an ItemsControl?
Thank you!
Add a property to your data class and databind it, then iterate over the collection itself.
public class myDataClass
{
public string Name { get; set;}
public bool IsSomething { get; set; }
}
<CheckBox x:Name="myCheckBox" Content="{Binding Name}" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
You can try something like traditional iteration:
public bool? TestMyCheckbox(string bindingName)
{
foreach (var item in myItemsControl.Items)
{
if (item.GetType() == typeof(CheckBox))
{
var checkbox = (CheckBox)item;
if (checkbox.Content.Equals(bindingName))
{
return (checkbox.IsChecked);
}
}
}
return null;
}
Additionaly (this may better fit your needs) you can look for a list of checkboxes bindings that are checked:
public IEnumerable<object> TestMyCheckboxes(ItemsControl control)
{
return from Control x in control.Items
where x.GetType().Equals(typeof(CheckBox)) && ((CheckBox)x).IsChecked == true
select ((CheckBox)x).Content;
}

Resources