How to disable selection a single item in a GridView - windows-8

How do you disable the selection single item from a GridView?
I have a GridView with it's ItemsSource bound to an IEnumerable<SampleDataItem>. I'd like to be able to programmatically not allow the selection of some items in the list while allowing selection of the others.

While I haven't done this, you should be able to use an ItemContainerStyleSelector on the GridView, the method gives you the container (GridViewItem) and the item you're binding to. From there you can set the IsEnabled property on the GridViewItem to false which makes it unselectable.
You'll also probably need to select a custom style as well since the default GridViewItem style will customise how a disabled item will look.
Update DataTemplateSelector Solution
public class IssueGridTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var selectorItem = container as SelectorItem;
if (item is Issue)
return IssueTemplate;
selectorItem.IsEnabled = false;
selectorItem.Style = RepositoryItemStyle;
return RepositoryTemplate;
}
public DataTemplate IssueTemplate
{
get;
set;
}
public DataTemplate RepositoryTemplate
{
get;
set;
}
public Style RepositoryItemStyle
{
get;
set;
}
}

Nigel's answer is great. I just added some attached properties to the WinRT XAML Toolkit that should make it simpler to do if you are populating your GridView using the ItemsSource property binding.
For me the usual way to modify the GridViewItem properties then was using GridView.ItemContainerStyle property. Using that method you would need to specify the IsEnabled property using a style and style setters don't support bindings in WinRT. Using the ItemContainerStyleSelector might be one way, but it requires defining a custom class.
I have created a GridViewItemExtensions class with an IsEnabled property that you can set on any control in your GridView.ItemTemplate like this:
xmlns:xyzc="using:Xyzzer.WinRT.Controls"
xyzc:GridViewItemExtensions.IsEnabled="{Binding IsEnabled}"
The property has a behavior of finding the GridViewItem in its ancestors visual tree and keeping its IsEnabled value synchronized to the GridViewItemExtensions.IsEnabled value set on its descendant.
Then as Nigel said - you still need to extract the template from a GridViewItem and modify it so the disabled items don't look out of place.

Related

WinrtXamlToolkit TreeView ItemContainerStyleSelector not firing

I am using the TreeView control from the WinrtXamlToolkit in a uwp app. I want to apply a different style to some TreeViewItems depending on a conditon so I created a class, TreeViewItemStyleSelector which derives from StyleSelector and I override the SelectStyleCore function.
public Style ResourceStyle { get; set; }
public Style ClassroomStyle { get; set; }
protected override Style SelectStyleCore(object item, DependencyObject container)
{
// control never reaches here.
// logic to apply style
}
Then in xaml I use it like this.
In Page Resources
<StyleSelectors:TreeViewItemStyleSelector ResourceStyle="{StaticResource AStyle}" ClassroomStyle = "{StaticResource BStyle}"/>
And later in the page.
<wxtControls:TreeView ItemsSource="{Binding StructureViewModels}" ItemContainterStyleSelector="{StaticResource TreeViewItemStyleSelector}" />
The problem is that the SelectStyleCore override is never called. Does anybody know why?
I am not yet sure what's the reason this doesn't work, although I have some theories. One is - this was never implemented. Perhaps at least at the root level it should work because it's an ItemsControl, but because of the way it's implemented (hierarchically) - the ItemContainerStyleSelector would have to be forwarded from the TreeView to the TreeViewItems, which it isn't.
I haven't had a chance to try to reproduce it yet, but if I were to try to fix it or work around it - I would first try forwarding that property in HeaderedItemsControl.cs - roughly where it says "// Note: this is where we would apply the HeaderTemplateSelector (...) (if implemented)". The alternative (if you don't want to modify the toolkit's code) might be to specify the template for the TreeViewItem and in the template - use a StyleSelector on template parts you want to be different for different data items.

Setting the initial selected item when binding to a ListView's SelectedItem property

I have a Xamarin.Forms xaml page in which I am using a ListView to allow the user to pick a single item out of a list. I have bound the ListView's SelectedItem property to a property on my ViewModel and this works fine. As soon as the user changes the selected item the property in my viewmodel updates as well.
However, even though I initially set the property in my ViewModel to one of the values from the list, when the page loads the ListView's SelectedItem property is null, which in turn sets the ViewModel property to null as well.
What I need is the other direction, I want the ListView to initially select the item that i've set in the VM property.
I can hack together a solution by writing extra code in the code behind file to explicitly set the initial selected item, but this introduces additional properties and complexity and is quite ugly.
What is the correct way to set the initial selected item of a ListView who's selected item is bound to a viewmodel property?
-EDIT-
I was asked to provide the code that I'm using for my binding.
It's very simple, standard:
<ListView x:Name="myList" ItemsSource="{Binding Documents}" SelectedItem="{Binding SelectedDocument}">
the view model that is set as the binding context for the listview is instantiated before the page is created and looks like this:
public class DocumentSelectViewModel : ViewModelBase
{
private Document selectedDocument;
public List<Document> Documents
{
get { return CachedData.DocumentList; }
}
public Document SelectedDocument
{
get { return selectedDocument; }
set { SetProperty(ref selectedDocument, value);
}
public DocumentSelectViewModel()
{
SelectedDocuement = CachedData.DocumentList.FirstOrDefault();
}
}
SetProperty is a function which simply rasies the INotifyPropertyChanged event if the new value is different from the old one, classical binding code.
I am a little rusty on XAML but don't you need to make the binding two-way?
E.G.
{ Binding SelectedDocument, Mode=TwoWay }
As long as the SelectedDocument property change raises the INotifyPropertyChanged event then you should get the desired effect.
If you replace
public DocumentSelectViewModel()
{
SelectedDocument = CachedData.DocumentList.FirstOrDefault();
}
By
public DocumentSelectViewModel()
{
SelectedDocument = Documents.FirstOrDefault();
}
Does it work for you ?
I had a similar problem that has been resolved this way...
You can use ctor DocumentSelectViewModel for set initial value. Honestly I dont like to make some job in ctor block but Xamarin.... You dont need DocumentSelectViewModel method. It will work.
public DocumentSelectViewModel ()
{
SelectedDocument = Documents[0]; //or any your desired.
}

Exposing commands for MVVM user control inside GridView in WinRT

I have a Windows Store application, following the MVVM pattern.
I have a Parent View (with matching Parent ViewModel) that contains a GridView control.
The ItemTemplate for that GridView control contains a Child View.
That child view contains a couple of buttons.
How do I wire it up such that when a user clicks a button on one of the ChildView controls, a method is called on the Parent ViewModel?
There are two methods for doing this.
first one that you can use is - bind your button to a command that is defined- in your parent viewmodel where you can do your work.
second one is - you can use mvvm messenger class. in which you have to send message from your button click eventhandler to your viewmodel. when you received this message add some eventhandler to it and perform your work there.
This is how I went about solving this problem.
Add an ICommand backed Dependency Property on the Child View code behind.
public static readonly DependencyProperty ChildButtonCommandProperty = DependencyProperty.Register("ChildButtonCommand", typeof(ICommand), typeof(ChildView),new PropertyMetadata(null, OnChildButtonCommandChanged));
public ICommand ChildButtonCommand
{
get { return (ICommand)GetValue(ChildButtonCommandProperty); }
set { SetValue(ChildButtonCommandProperty, value); }
}
private static void OnChildButtonCommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var self = (ChildView)sender;
self.ChildButtonCommand.Command = (ICommand)e.NewValue;
}
In the Parent ViewModel, add a public getter property of type ICommand, implemented with a RelayCommand that you can find here: https://relaycommandrt.codeplex.com/
In the Xaml of the Parent View, bind the ChildButtonCommand in the Child View:
<GridView.ItemTemplate>
<DataTemplate>
<views:ChildView ChildButtonCommand="{Binding ElementName=ParentView, Path=DataContext.PropertyOnParentViewModel}"/>
</DataTemplate>
Examine the binding syntax closely. Since we're in a DataTemplate for the GridView Item, our DataContext is not the Parent View Model.(It's the child item objects). If we want to bind the button command to the Parent View Model, we need a reference to something in our parent view. In this case, I named the view "ParentView". Using the Binding ElementName syntax, I could bind to the DataContext of the ParentView and more specifically a property on the ParentViewModel.

Silverlight 5: Binding command to listboxitem

I am beginner in silverlight and all this mvvm pattern is bit confusing.
In my application I have two listboxs one for country and one for states.
What I want to do is when I select a Country from the listbox1 second listbox will display states from the selected country.
i.e I want to bind command in xaml to listboxitem.
I try to find the solution by Google but either solutions was too complex for me to understand or using different mvvm pattern like prism,light etc.
There are a few different ways of doing this:
1: (Easiest!) Bind the SelectedItem of the first ListBox to your ViewModel. In the Setter for the ViewModel property, change the list that you're binding to the second listbox. Note that your ViewModel property will need to use INotifyPropertyChanged to notify that the list has changed.
Eg: If your xaml looks like:
<ListBox ItemSource="{Binding ListOne}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
<ListBox ItemSource="{Binding ListTwo}"/>
Then your ViewModel might be a bit like:
public List<MyItem> ListOne { get; set; }
private MyItem _selectedItem
public MyItem SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
ListTwo = _selectedItem.SubItems;
}
}
private List<MyOtherItem> _listTwo
public List<MyOtherItem> ListTwo
{
get { return _listTwo; }
set
{
_listTwo = value;
RaisePropertyChanged("ListTwo");
}
}
2: If the data for the second list is literally a property of the items in the first list, you can use an Binding in xaml to directly join them up.
ItemSource="{Binding Path=SelectedItem.MyItemsProperty, ElementName=MyFirstListBoxName}"
3: You can use an EventTrigger with an EventToCommand to turn the SelectedItemChanged event into a Command execution. You're not literally binding a command to the ListBoxItem, you're binding the command to the change.
I would recommend the first option, it's easiest and gives you good control of what's going on without getting too complicated.

MVVM Silverlight - can not assign existing control to a property of collection of custom elements in XAML

I have a class
public class Item
{
public string A { get; set; }
public Control B { get; set; }
}
I'm using MVVM with Silverlight. I have a custom view that is inherited from a standard view. Custom view has public property public ICollection MyItems { get; set; } which should store items described above.
In xaml of my view I have
xxxx.MyItems>
Item A="someText" B="_existingButton" />
Item A="someText2" B="_existingButton2" />
/xxxx.MyItems>
Initialize() method of View fails when trying to assign value for B.
How can I assign a reference to existing element for a custom collection item?
I don't exactly understand what you are trying to achieve, but to help you arrive at a solution, I recommend that you attempt your task in the code behind file first (i.e. in the .xaml.cs) file.
By doing this, you will be given much more informative help from the compiler and intellisense.
Once you've achieved what you wanted in the code behind, then try and implement it in the xaml file.