WinRT XAML Toolkit BindableSelections not updating UI - xaml

Below is the xaml and c# code for handling selected items in my gridview.
I am also using MVVM Light and everything is working, including me being able to see what's inside SelectedItems.
However I when I attempt to clear the SelectedItems, my UI doesn't seem to update/reflect the changes made to SelectedItems.
I am using WinRT XAML Toolkit (http://winrtxamltoolkit.codeplex.com/) which has the BindableSelection extension on a GridView
XAML
<controls:CustomGridView
x:Name="VideoItemGridView"
Grid.Row="2"
Margin="0,-3,0,0"
Padding="116,0,40,46"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsItemClickEnabled="True"
SelectionMode="Extended"
Extensions:GridViewExtensions.BindableSelection="{Binding SelectedVideoItems, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource ViewSource}}"
ItemTemplate="{StaticResource VideoItemTemplate}">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid ItemWidth="250" ItemHeight="160" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</controls:CustomGridView>
MyViewViewModel.cs
#region Selected Items
/// <summary>
/// Gets or sets the selected video items.
/// </summary>
public ObservableCollection<object> SelectedVideoItems
{
get { return this._selectedVideoItems; }
set
{
this._selectedVideoItems = value;
this.Set("SelectedVideoItems", ref this._selectedVideoItems, value);
}
}
private ObservableCollection<object> _selectedVideoItems = new ObservableCollection<object>();
#endregion
#region App Bar Click Commands
/// <summary>
/// Gets the ClearSelection click command.
/// </summary>
public ICommand ClearSelectionClickCommand
{
get
{
return new RelayCommand(() => this.ClearSelectionOperation());
}
}
/// <summary>
/// Selects all command operation.
/// </summary>
private void ClearSelectionOperation()
{
this.SelectedVideoItems = new ObservableCollection<object>();
}
#endregion

Try clearing your selected items in ClearSelectionOperation by calling
this.SelectedVideoItems.Clear();
instead of
this.SelectedVideoItems = new ObservableCollection<object>();
If that doesn't help check if the current version of the extension from March 7 fixes the problem.

It turns out that since I am using a data template, it is actually my data model that needed to set a flag to indicate it is selected
Here's the missing piece of the puzzle. Once I update the data model bound to the grid view item (which also includes support for row/col spanning), the UI updated as expected.
Hope this helps others.
public class CustomGridView : GridView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
try
{
base.PrepareContainerForItemOverride(element, item);
dynamic _Item = item;
element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, _Item.ColumnSpan);
element.SetValue(VariableSizedWrapGrid.RowSpanProperty, _Item.RowSpan);
element.SetValue(GridViewItem.IsSelectedProperty, _Item.IsSelected);
}
catch
{
element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, 1);
element.SetValue(VariableSizedWrapGrid.RowSpanProperty, 1);
element.SetValue(GridViewItem.IsSelectedProperty, false);
}
finally
{
base.PrepareContainerForItemOverride(element, item);
}
}

Related

uwp AutoSuggestBox ItemsSource not working

I have created a simple UWP app. All I want is, whenever the user clicks on the AutoSuggestBox, it needs to show all the items in the ItemsSource as suggestions. A combobox is working fine when I set the DisplayMemberPath but the AutoSuggestBox upon clicking doesn't show the ItemsSource as suggestions. Please help. (In this case when I click on the AutoSuggestBox, it needs to display ABC, BCD)
xaml: MainPage.xaml
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<ComboBox Width="120"
ItemsSource="{Binding TestList,Mode=OneWay}"
SelectedIndex="0"
DisplayMemberPath="Name"/>
<AutoSuggestBox Width="200"
Margin="0,20,0,0"
ItemsSource="{Binding TestList,Mode=OneWay}"
DisplayMemberPath="Name"
TextMemberPath="Name"
PlaceholderText="Search"/>
</StackPanel>
Code Behind: MainPage.xaml.cs
public class Test
{
public string Name { get; set; }
public int Id { get; set; }
}
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private List<Test> _testList;
public List<Test> TestList
{
get
{
_testList = new List<Test>();
_testList.Add(new Test()
{
Name = "ABC",
Id = 1,
});
_testList.Add(new Test()
{
Name = "BCD",
Id = 2,
});
return _testList;
}
}
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
}
In this case when I click on the AutoSuggestBox, it needs to display ABC, BCD
Please refer AutoSuggestBox document, Represents a text control that makes suggestions to users as they enter text using a keyboard or pen (using ink and handwriting recognition). it will filter your data based on the input. Then, set the filtered data as the ItemsSource of the AutoSuggestBox to update the suggestion list. So the drop list only rendered when you typing. and it will not display the drop-list when you click the AutoSuggestBox.
For this scenario, the best practice is using ComboBox to replace.

Load only selected item in a XAML FlipView

Is there a way, for the FlipView control, to ONLY have it load the selected item??
The default style of a FlipView, from Microsoft's styles, uses a VirtualizingStackPanel:
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel AreScrollSnapPointsRegular="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
What occurs is that the current element and adjacent elements will begin to load. What I would like for the FlipView to do is only load the item when it's shown (in other words, when it becomes the selected item).
Is that possible?
You could make your custom classes:
public class ImageGallery : FlipView
{
public ImageGallery()
{
SelectionChanged += (s, a) =>
{
((ImageItem)Items[SelectedIndex]).Load()
}
}
}
public class ImageItem : FlipViewItem
{
public ImageItem(SomeType yourImageInfo)
{
Content = new YourControl(yourImageInfo);
}
public void Load()
{
//load your image
}
}
Here's my problem:
I'm using a flipview which contains items that consist of a custom control that load an image asynchronously. I only want to load the image of the selected index. So when the flipview loads, the first item loads. If the user swipes left, now the second image loads, and so on.
If you have a data-bound ImageSource, you can do it in such a way, that you manually force the load of the current item when the selection of the FlipView changes.
You can create a custom item class. Note that it implements the INotifyPropertyChanged interface so that it notifies the control when the image is loaded:
public class FlipViewItemViewModel : INotifyPropertyChanged
{
private bool _isLoaded = false;
private ImageSource _imageSource = null;
public ImageSource ImageSource
{
get
{
return _imageSource;
}
set
{
_imageSource = value;
OnPropertyChanged();
}
}
/// <summary>
/// Forces the loading of the item
/// </summary>
public void ForceLoad()
{
//prevent loading twice
if ( !_isLoaded )
{
_isLoaded = true;
//load the image (probably from network?)
ImageSource = new BitmapImage(
new Uri( "ms-appx:///Assets/StoreLogo.png" ) );
}
}
/// <summary>
/// INotifyPropertyChanged implementation
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(
[CallerMemberName] string propertyName = null )
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
In XAML, you have to bind the data source to your FlipView using ItemsSource and handle the SelectionChanged event:
<FlipView x:Name="FlipControl"
ItemsSource="{x:Bind Items}"
SelectionChanged="Selector_OnSelectionChanged">
Inside the SelectionChanged handler you then manually call the ForceLoad method for the current item.
private void Selector_OnSelectionChanged( object sender, SelectionChangedEventArgs e )
{
//get the currently selected item
var currentItem = FlipControl.SelectedItem as FlipViewItemViewModel;
//force-load it
currentItem?.ForceLoad();
}
I have made a GitHub sample with this solution and example.

Windows 8.1: Behaviors on Flyouts don't Work

I am developing a windows 8.1 app using VS 2013 and MVVM Light.
The following code shows the behavior in a flyout within an appbar:
<AppBarButton.Flyout>
<Flyout x:Name="FlyoutCalculator"
Placement="Top"
FlyoutPresenterStyle="{StaticResource FlyoutPresenterBaseStyle}">
<uc:Calculator ApplyCommand="{Binding CancelCommand}"
CancelCommand="{Binding CancelCommand}"
Available="{Binding AvailableCounter, Mode=OneWay}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Opening">
<core:InvokeCommandAction Command="{Binding ShowCurrentCostsCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Flyout>
</AppBarButton.Flyout>
Unfortunately I get an exception while compiling the app:
WinRT-Informationen: Cannot add instance of type Microsoft.Xaml.Interactions.Core.EventTriggerBehavior to a collection of type Microsoft.Xaml.Interactivity.BehaviorCollection
Other Behaviors in the View do work, does someone know a solution to this?
Extremely late answer here, but I had the same issue and came up with a solution after finding this post.
I just created a custom behavior specifically for flyouts, used like this. OpenActions will execute when the flyout is opened, and CloseActions will execute when the flyout closes. In this case, I wanted the bottom app bar to not be visible when the flyout was open.
<Flyout Placement="Full">
<i:Interaction.Behaviors>
<behaviors:FlyoutBehavior>
<behaviors:FlyoutBehavior.OpenActions>
<core:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" TargetObject="{Binding ElementName=CommandBar}" />
</behaviors:FlyoutBehavior.OpenActions>
<behaviors:FlyoutBehavior.CloseActions>
<core:ChangePropertyAction PropertyName="Visibility" Value="Visible" TargetObject="{Binding ElementName=CommandBar}" />
</behaviors:FlyoutBehavior.CloseActions>
</behaviors:FlyoutBehavior>
</i:Interaction.Behaviors>
<Grid>
...
</Grid>
</Flyout>
Code is here:
class FlyoutBehavior : DependencyObject, IBehavior
{
public DependencyObject AssociatedObject { get; private set; }
public void Attach(Windows.UI.Xaml.DependencyObject associatedObject)
{
var flyout = associatedObject as FlyoutBase;
if (flyout == null)
throw new ArgumentException("FlyoutBehavior can be attached only to FlyoutBase");
AssociatedObject = associatedObject;
flyout.Opened += FlyoutOpened;
flyout.Closed += FlyoutClosed;
}
public void Detach()
{
var flyout = AssociatedObject as FlyoutBase;
if (flyout != null)
{
flyout.Opened -= FlyoutOpened;
flyout.Closed -= FlyoutClosed;
}
}
public static readonly DependencyProperty OpenActionsProperty =
DependencyProperty.Register("OpenActions", typeof(ActionCollection), typeof(FlyoutBehavior), new PropertyMetadata(null));
public ActionCollection OpenActions
{
get { return GetValue(OpenActionsProperty) as ActionCollection; }
set { SetValue(OpenActionsProperty, value); }
}
public static readonly DependencyProperty CloseActionsProperty =
DependencyProperty.Register("CloseActions", typeof(ActionCollection), typeof(FlyoutBehavior), new PropertyMetadata(null));
public ActionCollection CloseActions
{
get { return GetValue(CloseActionsProperty) as ActionCollection; }
set { SetValue(CloseActionsProperty, value); }
}
private void FlyoutOpened(object sender, object e)
{
foreach (IAction action in OpenActions)
{
action.Execute(AssociatedObject, null);
}
}
private void FlyoutClosed(object sender, object e)
{
foreach (IAction action in CloseActions)
{
action.Execute(AssociatedObject, null);
}
}
public FlyoutBehavior()
{
OpenActions = new ActionCollection();
CloseActions = new ActionCollection();
}
}
I do not have a solution but:
I'm not using Flyouts in my Windows 8.1 App, I'm using a UserControl on which I have added a EventTriggerBehavior as you did. And I get exactly the same Errormessage from VisualStudio at runtime.
As I am using a RoutedEventHandler this could cause the Problem as you use
EventHandler<object> Opening
as the Trigger for the Behavior. But that is just an idea of what is the problem.
For me I have found an answer:
I have changed the Type of my RoutedEventHandler to be just a normal EventHandler. And the Method inside the CodeBehind which triggers the RoutedEventHandler is invoked with only the sender, because I dont know how to convert RoutedEventArgs into EventArgs, but as long as I dont need the EventArgs it's not a problem.
You could also make a workaround by creating a UserControl with a Flyout Control and make the Opening Event public to the Page where you use it. Then you can add the EventTriggerBehavior to the UserControl and connect it to your custom Opening Event and you should get the expected behavior.

GridView doesn't show items async loader, doesn´t refresh items

I'm developing a Win8 App:
I have a datasource class that take ítems from webservice via json:
public class DataSourceCapitulos
{
public ObservableCollection<capitulo> ListaCapitulos { get; set; }
public DataSourceCapitulos()
{
CargarCapitulos();
}
public async void CargarCapitulos()
{
var resourceUri = Cie10Uri.CapitulosUri;
HttpClient httpClient = new HttpClient();
bool error = false;
HttpRequestException exception = null;
try
{
string response = await httpClient.GetStringAsync(resourceUri);
ListaCapitulos = new ObservableCollection<capitulo>(JsonConvert.DeserializeObject<List<capitulo>>(response));
}
catch (HttpRequestException e)
{
error = true;
exception = e;
}
if (error)
{
MessageDialog adv = new MessageDialog(string.Format("La consulta {0}, devolvió:{1}", resourceUri, exception.Message), "No se pudo consultar!!! ");
adv.Commands.Add(
new UICommand("Ok")
);
await adv.ShowAsync();
}
}
}
And have a XAML form that has this source:
<Page.Resources>
<data:DataSourceCapitulos x:Key="DataSourceCapitulos"></data:DataSourceCapitulos>
</Page.Resources>
And Finally a GridView with it's source ítems pointing to ListaCapitulos property of DataSourceCapitulos as this:
<GridView Grid.Column="1" Grid.Row="1" ItemsSource="{Binding Source={StaticResource DataSourceCapitulos},Path=ListaCapitulos}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Height="250" Width="250">
<Grid.Background>
<ImageBrush ImageSource="{Binding Imagen}"/>
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="90"/>
</Grid.RowDefinitions>
<Rectangle >
<Rectangle.Fill>
<SolidColorBrush Color="#FF122951" Opacity="0.6"/>
</Rectangle.Fill>
</Rectangle>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</GridView>
Until Here my app run without problema, but the problem is it doesn´t show te ítems, even the ListaCapitulos was populated as I expected.
THis is MainPage.xaaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
/// <summary>
/// Se invoca cuando esta página se va a mostrar en un objeto Frame.
/// </summary>
/// <param name="e">Datos de evento que describen cómo se llegó a esta página. La propiedad Parameter
/// se usa normalmente para configurar la página.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
There is something that i Miss ?
In XAML, set ItemsSource="{Binding listaCapitulos} (the ObservableCollection).
in the C# file of the page you need an instance of class DataSourceCapitulos, like DataSourceCapitulos ChaptersVm = new DataSourceCapitulos(); (as a member, you need to access it more than once).
set the gridview's (the one you have in XAML which should show the chapters) DataContext to that instance. You can do this in C#, ChaptersGridView.DataContext = ChaptersVm.
You're saying you know you have the chapters in ListaCapitulos, that means you didn't bind them correctly to be shown.
Also, I see async void CargarCapitulos() is meant to be async and you are calling it in the constructor but will run synchronously. Your app might not run fluidly when gathering the chapters.
UPDATE
Altough I am not very sure of first way, I can think of two methods you can keep the call of Cargar Capitulos async.
1) Await the method call in a new method (I am not very sure this would do the trick) :
In constructor :
public DataSourceCapitulos()
{
LoadCapitulos();
}
where in LoadCapitulos body you have :
public async void LoadCapitulos()
{
//this awaits the chapters to load (you were missing await,
//but you can't use await in a constructor, so this is a work-around
await CargarCapitulos();
}
If 1) doesn't work try 2) which will work definitely but you must do some changes :
2) Your ObservableCollection will not notify the UI of any update if you do some changes on the collection with the chapters. To do so, you must use the INotifyPropertyChanged interface and implement its members. If you used an advanced template of the Windows 8 App, in the Common folder, you have a class called BindableBase - try to use it directly by inheriting it. Also, you must use the OnPropertyChanged() event call in the ObservableCollectionsetter, so that when the chapters collection changed, the UI will also be changed through binding :
public class DataSourceCapitulos : BindableBase
{
private ObservableCollection <capitulo> _listaCapitulos;
public ObservableCollection <capitulo> ListaCapitulos
{
get
{
return _listaCapitulos;
}
set
{
_listaCapitulos = value;
OnPropertyChanged(); //This notifies of changes of collection
}
}
However, if you don't have the BindableBase, just implement INotifyPropertyChanged and the rest will look just like I wrote before. With these changes that provide notifications, the chapters should show on the UI, even if they are loaded later, after the UI has loaded.
But I'd say you SHOULD implement it for the notification changes to have any effect. Otherwise, you could have used a simple List of Capitulos (freaky spanish) instead of Observable Collection.
I think the view(xaml form) is not getting aware of data change through this binding.
Why you are using a static resource?
you can straightly set ListaCapitulos to the girid's data source ,otherwise, you should use a MVVM model ,and you should aware your view of property change.

ComboBox SelectedValue doesn't show

I have a strange problem in my WinRT/C# XAML Metro app, using the Windows 8 Release Preview (latest patches installed). I'm using a ComboBox, whose values ItemsSource and SelectedValue are bound to properties in a ViewModel:
<ComboBox SelectedValue="{Binding MySelectedValue, Mode=TwoWay}"
ItemsSource="{Binding MyItemsSource, Mode=OneWay}"
Width="200" Height="30" />
Code behind:
public MainPage()
{
this.InitializeComponent();
DataContext = new TestViewModel();
}
And a very simple definition of the TestViewModel, using strings:
public class TestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private IEnumerable<string> _myItemsSource = new List<string>
{
"Test Item 1",
"Test Item 2",
"Test Item 3"
};
public IEnumerable<string> MyItemsSource
{
get { return _myItemsSource; }
}
private string _mySelectedValue = "Test Item 2";
public string MySelectedValue
{
get { return _mySelectedValue; }
set
{
_mySelectedValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("MySelectedValue"));
}
}
}
}
Now I thought this simple solution should just work... But when I start the app, the SelectedValue="Test Item 2" doesn't show up, the ComboBox is left empty. By setting breakpoints I noticed that the bound values MyItemsSource and MySelectedValue are corectly retrieved from the View Model when I set the DataContext of the view. After this action, the ComboBox.SelectedValue property is actually set to "Test Item 2", but it just doesn't show! Also I noticed that when I change the selected value in the ComboBox by user action on the UI, the changed value shows up in the ComboBox and the View Model property is updated accordingly. So everything seems to work fine except the initial visualization of the MySelectedValue View Model property. I'm becoming really desperate about that...
Now while this is the simplest example, in the origin I wanted to bind whole entities to ComboBox, setting DisplayMemberPath and SelectedValuePath. Unfortunately, the same problem occurs.
I found the problem in my example: In the XAML markup I've defined the SelectedValue property before the ItemsSource property. If I swap both definitions in this way, it works:
<ComboBox ItemsSource="{Binding MyItemsSource, Mode=OneWay}"
SelectedValue="{Binding MySelectedValue, Mode=TwoWay}"
Width="200" Height="30" />
This is really odd and annoying. Now I would like to know: is this a bug or by design? I think this is a bug, because the control should be working regardless of the order of the defined properties in XAML.
this is working solution : you can find here https://skydrive.live.com/?cid=b55690d11b67401d&resid=B55690D11B67401D!209&id=B55690D11B67401D!209
<ComboBox Width="300" Height="32" HorizontalAlignment="Left" DisplayMemberPath="Name"
VerticalAlignment="Top" ItemsSource="{Binding PersonCollection}"
SelectedItem="{Binding SelectedPerson, Mode=TwoWay}"></ComboBox>
ViewModle class is
public class ViewModel:BaseViewModel
{
private Person selectedPerson;
public Person SelectedPerson {
get { return this.selectedPerson; }
set { this.selectedPerson = value;
this.RaisePropertyChanged("SelectedPerson");
}
}
public ObservableCollection<Person> PersonCollection { get; set; }
public ViewModel()
{
this.PersonCollection = new ObservableCollection<Person>();
this.PopulateCollection();
//setting first item as default one
this.SelectedPerson = this.PersonCollection.FirstOrDefault();
}
private void PopulateCollection()
{
this.PersonCollection.Add(new Person { Name="Oscar", Email="oscar#sl.net" });
this.PersonCollection.Add(new Person { Name = "Jay", Email = "jay#sl.net" });
this.PersonCollection.Add(new Person { Name = "Viral", Email = "viral#sl.net" });
}
}