Managing persistent navigation views (MVVM, WPF) - mvvm

I'm slowly learning MVVM in WPF. I code a GBA game editor. The editor consist of a main window (Editor.xaml) and depending which editor is selected in the menu, I'd like to display the corresponding persistent view (that is not destroyer when switching).
I'm trying to get working the TabControlEx class found in a few posts on SO such as here and here.
However I run into two problem: first, tabControlEx selectedItem does not change (fixed see edit) and second it seems on the TabItem OnMouseOver I lose the View (having a color Background property for the TabItem will switch to white color).
Now I'm pretty sure I'm missing something more or less obvious, but being new to MVVM I don't really know where to look. So here the code breakdown.
TabControlEx.cs
[TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))]
public class TabControlEx : System.Windows.Controls.TabControl
{
// Holds all items, but only marks the current tab's item as visible
private Panel _itemsHolder = null;
// Temporaily holds deleted item in case this was a drag/drop operation
private object _deletedObject = null;
public TabControlEx()
: base()
{
// this is necessary so that we get the initial databound selected item
this.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
/// <summary>
/// if containers are done, generate the selected item
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
UpdateSelectedItem();
}
}
/// <summary>
/// get the ItemsHolder and generate any children
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_itemsHolder = GetTemplateChild("PART_ItemsHolder") as Panel;
UpdateSelectedItem();
}
/// <summary>
/// when the items change we remove any generated panel children and add any new ones as necessary
/// </summary>
/// <param name="e"></param>
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
if (_itemsHolder == null)
{
return;
}
switch (e.Action)
{
case NotifyCollectionChangedAction.Reset:
_itemsHolder.Children.Clear();
if (base.Items.Count > 0)
{
base.SelectedItem = base.Items[0];
UpdateSelectedItem();
}
break;
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
// Search for recently deleted items caused by a Drag/Drop operation
if (e.NewItems != null && _deletedObject != null)
{
foreach (var item in e.NewItems)
{
if (_deletedObject == item)
{
// If the new item is the same as the recently deleted one (i.e. a drag/drop event)
// then cancel the deletion and reuse the ContentPresenter so it doesn't have to be
// redrawn. We do need to link the presenter to the new item though (using the Tag)
ContentPresenter cp = FindChildContentPresenter(_deletedObject);
if (cp != null)
{
int index = _itemsHolder.Children.IndexOf(cp);
(_itemsHolder.Children[index] as ContentPresenter).Tag =
(item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item));
}
_deletedObject = null;
}
}
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
_deletedObject = item;
// We want to run this at a slightly later priority in case this
// is a drag/drop operation so that we can reuse the template
this.Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
new Action(delegate ()
{
if (_deletedObject != null)
{
ContentPresenter cp = FindChildContentPresenter(_deletedObject);
if (cp != null)
{
this._itemsHolder.Children.Remove(cp);
}
}
}
));
}
}
UpdateSelectedItem();
break;
case NotifyCollectionChangedAction.Replace:
throw new NotImplementedException("Replace not implemented yet");
}
}
/// <summary>
/// update the visible child in the ItemsHolder
/// </summary>
/// <param name="e"></param>
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
UpdateSelectedItem();
}
/// <summary>
/// generate a ContentPresenter for the selected item
/// </summary>
void UpdateSelectedItem()
{
if (_itemsHolder == null)
{
return;
}
// generate a ContentPresenter if necessary
TabItem item = GetSelectedTabItem();
if (item != null)
{
CreateChildContentPresenter(item);
}
// show the right child
foreach (ContentPresenter child in _itemsHolder.Children)
{
child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed;
}
}
/// <summary>
/// create the child ContentPresenter for the given item (could be data or a TabItem)
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
ContentPresenter CreateChildContentPresenter(object item)
{
if (item == null)
{
return null;
}
ContentPresenter cp = FindChildContentPresenter(item);
if (cp != null)
{
return cp;
}
// the actual child to be added. cp.Tag is a reference to the TabItem
cp = new ContentPresenter();
cp.Content = (item is TabItem) ? (item as TabItem).Content : item;
cp.ContentTemplate = this.SelectedContentTemplate;
cp.ContentTemplateSelector = this.SelectedContentTemplateSelector;
cp.ContentStringFormat = this.SelectedContentStringFormat;
cp.Visibility = Visibility.Collapsed;
cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item));
_itemsHolder.Children.Add(cp);
return cp;
}
/// <summary>
/// Find the CP for the given object. data could be a TabItem or a piece of data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
ContentPresenter FindChildContentPresenter(object data)
{
if (data is TabItem)
{
data = (data as TabItem).Content;
}
if (data == null)
{
return null;
}
if (_itemsHolder == null)
{
return null;
}
foreach (ContentPresenter cp in _itemsHolder.Children)
{
if (cp.Content == data)
{
return cp;
}
}
return null;
}
/// <summary>
/// copied from TabControl; wish it were protected in that class instead of private
/// </summary>
/// <returns></returns>
protected TabItem GetSelectedTabItem()
{
object selectedItem = base.SelectedItem;
if (selectedItem == null)
{
return null;
}
if (_deletedObject == selectedItem)
{
}
TabItem item = selectedItem as TabItem;
if (item == null)
{
item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem;
}
return item;
}
}
My main view:
Editor.xaml
<Window.Resources>
<ResourceDictionary Source="Resources/GlobalDictionary.xaml" />
</Window.Resources>
<DockPanel>
<DockPanel DockPanel.Dock="Top" KeyboardNavigation.TabNavigation="None">
<Menu Height="20" KeyboardNavigation.TabNavigation="Cycle">
<MenuItem Header="{StaticResource MenuFile}">
<MenuItem Header="{StaticResource MenuOpen}" />
<MenuItem Header="{StaticResource MenuSave}" />
<MenuItem Header="{StaticResource MenuExit}" Command="{Binding Path=CloseCommand}" />
</MenuItem>
<MenuItem Header="{StaticResource MenuEditors}">
<MenuItem Header="{StaticResource MenuMonster}" Command="{Binding Path=OpenMonsterEditor}"/>
</MenuItem>
<MenuItem Header="{StaticResource MenuHelp}">
<MenuItem Header="{StaticResource MenuAbout}" />
<MenuItem Header="{StaticResource MenuContact}" />
</MenuItem>
</Menu>
</DockPanel>
<controls:TabControlEx ItemsSource="{Binding AvailableEditors}"
SelectedItem="{Binding CurrentEditor}"
Style="{StaticResource BlankTabControlTemplate}">
</controls:TabControlEx>
</DockPanel>
GlobalDictionary.xaml
<DataTemplate DataType="{x:Type vm:GBARomViewModel}">
<vw:GBARomView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MonsterViewModel}">
<vw:MonsterView />
</DataTemplate>
<Style x:Key="BlankTabControlTemplate" TargetType="{x:Type control:TabControlEx}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type control:TabControlEx}">
<DockPanel>
<!-- This is needed to draw TabControls with Bound items -->
<StackPanel IsItemsHost="True" Height="0" Width="0" />
<Grid x:Name="PART_ItemsHolder" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I'm not sure if the following is right, but each view extent TabItem as follow. The first view on startup is shown correctly.
GBARomView.xaml
<TabItem x:Class="FF6AE.View.GBARomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FF6AE.View"
mc:Ignorable="d"
d:DesignHeight="380" d:DesignWidth="646"
Background="BlueViolet">
<Grid >
</Grid>
</TabItem>
GBARomView.xaml.cs
public partial class GBARomView : TabItem
{
public GBARomView()
{
InitializeComponent();
}
}
Finally my main viewModel:
EDitorViewModel.cs
public class EditorViewModel: ViewModelBase
{
ViewModelBase _currentEditor;
ObservableCollection<ViewModelBase> _availableEditors;
RelayCommand _closeCommand;
RelayCommand _OpenMonsEditorCommand;
public EditorViewModel()
{
base.DisplayName = (string)AppInst.GetResource("EditorName");
_availableEditors = new ObservableCollection<ViewModelBase>();
_availableEditors.Add(new GBARomViewModel());
_availableEditors.Add(new MonsterViewModel());
_availableEditors.CollectionChanged += this.OnAvailableEditorsChanged;
_currentEditor = _availableEditors[0];
_currentEditor.PropertyChanged += this.OnSubEditorChanged;
}
public ViewModelBase CurrentEditor
{
get
{
if (_currentEditor == null)
{
_currentEditor = new GBARomViewModel();
_currentEditor.PropertyChanged += this.OnSubEditorChanged;
}
return _currentEditor;
}
set
{
_currentEditor = value;
// this is the thing I was missing
OnPropertyChanged("CurrentEditor");
}
}
void OnSubEditorChanged(object sender, PropertyChangedEventArgs e)
{
// For future use
}
public ObservableCollection<ViewModelBase> AvailableEditors
{
get
{
return _availableEditors;
}
}
void OnAvailableEditorsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// For future use
}
public ICommand OpenMonsterEditor
{
get
{
if (_OpenMonsEditorCommand == null)
_OpenMonsEditorCommand = new RelayCommand(param => this.OpenRequestOpen());
return _OpenMonsEditorCommand;
}
}
void OpenRequestOpen()
{
_currentEditor = _availableEditors[1];
}
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
_closeCommand = new RelayCommand(param => this.OnRequestClose());
return _closeCommand;
}
}
public event EventHandler RequestClose;
void OnRequestClose()
{
EventHandler handler = this.RequestClose;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
So basically I am lost why clicking on the monster editor in the menu does not switch the view though currentEditor value is changed (fixed) and why on mouse over on the tabItem I lose the background testing color of the view (it turns to white). <- still not fixed!
Any help would be appreciated. Thanks in advance.
Edit: I was missing the OnPropertyChanged("CurrentEditor"); in the setter of CurrentEditor.

Well basically my two questions were misunderstandings. First, the CurrentEditor attribute need OnPropertyChanged called in EditorViewModel.cs such as follow. This calls the ViewModelBase class OnPropertyChanged method:
public ViewModelBase CurrentEditor
{
get
{
if (_currentEditor == null)
{
_currentEditor = new GBARomViewModel();
_currentEditor.PropertyChanged += this.OnSubEditorChanged;
}
return _currentEditor;
}
set
{
_currentEditor = value;
OnPropertyChanged("CurrentEditor");
}
}
My second problem took longer to solve. I did not needed to have my views extent TabItem but instead something like DockPanel. This ensure the whole window will not have the TabItem IsMouseOver behavior of reverting the color back to default. Setting an background image such as follow work and I can navigate the view. I wasn't able to solve this with a control template of TabItem.
GbaRomView.xaml.cs
public partial class GBARomView : DockPanel
{
public GBARomView()
{
InitializeComponent();
}
}
GbaRomView.xaml
<DockPanel x:Class="FF6AE.View.GBARomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DesignHeight="380" d:DesignWidth="646">
<DockPanel.Background>
<ImageBrush ImageSource="/Resources/Images/DefaultBackground.png"></ImageBrush>
</DockPanel.Background>
</DockPanel>

Related

Bind ProgressRing to mvvm

I am downloading data from an api, and displaying that data in the view. As I wait I want to display a ProgressRing, but when I bind it dosen't work
Bind the ring to the cityData property, with a two way mode and update it when the property changes
Created a new property in the VM, that is true by default and it will turn false when I get the data back
XAML
<TextBox x:Name="currentLocation"
PlaceholderText="Please wait..."
IsReadOnly="True"
Margin="20"
Width="300"
Text="{Binding cityData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ListView RelativePanel.Below="currentLocation"
x:Name="ForecastList"
Margin="20"
SelectedItem="{Binding currentDay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding dailyForecasts}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<TextBlock x:Name="dateTB" Text="{Binding Date.DayOfWeek}" />
<TextBlock x:Name="highTB" Text="{Binding Temperature.Maximum.Value, Converter={StaticResource cv}}" FontSize="10" />
<TextBlock x:Name="lowTB" FontSize="10" Text="{Binding Temperature.Minimum.Value, Converter={StaticResource cv}}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ProgressRing x:Name="pRing" RelativePanel.Above="ForecastList" RelativePanel.AlignHorizontalCenterWith="currentLocation" IsActive="{Binding ring, Mode=TwoWay}" RelativePanel.Below="currentLocation" />
VM
public class WeatherVM: INotifyPropertyChanged
{
public AccuWeather accuWeather { get; set; }
private string _cityData;
public string cityData
{
get { return _cityData; }
set
{
if (value != _cityData)
{
_cityData = value;
onPropertyChanged("cityData");
GetWeatherData();
}
}
}
private DailyForecast _currentDay;
public DailyForecast currentDay
{
get { return _currentDay; }
set
{
if (value != _currentDay) \
{
_currentDay = value;
onPropertyChanged("currentDay");
}
}
}
public bool ring { get; set; } = true;
public ObservableCollection<DailyForecast> dailyForecasts { get; set; }
public WeatherVM()
{
GetCuurentLocation();
dailyForecasts = new ObservableCollection<DailyForecast>();
}
private async void GetCuurentLocation() {
cityData = await BingLocator.GetCityData();
}
public async void GetWeatherData() {
var geoposition = await LocationManager.GetGeopositionAsync();
var currentLocationKey = await WeatherAPI.GetCityDstaAsync(geoposition.Coordinate.Point.Position.Latitude, geoposition.Coordinate.Point.Position.Longitude);
var weatherData = await WeatherAPI.GetWeatherAsync(currentLocationKey.Key);
if (weatherData != null) {
foreach (var item in weatherData.DailyForecasts) {
dailyForecasts.Add(item);
}
}
currentDay = dailyForecasts[0];
ring = false;
}
public event PropertyChangedEventHandler PropertyChanged;
private void onPropertyChanged(string property) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
The progress ring appears when the app launch, and disappear when the data is returned
From your code, it seems to be a layout issue. First you put ListView below currentLocation, then set ProgressRing above ListView and below currentLocation, so the height of ProgressRing wil be zero. I'm not clear about your layout, you can try to only set RelativePanel.Above="ForecastList" for ProgressRing to see if it will appear.
You can use Windows Community Toolkit control for showing progress ring (Busy indicator).
<Page ...
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"/>
<controls:Loading x:Name="LoadingControl" IsLoading="{Binding IsBusy}">
<!-- Loading screen content -->
</controls:Loading>

xamarin form: Custom View in listview on android screw up

I found xamarin render ui is very difficult.
I have a custom view inside the listview which contain a list of small icons. Each item may have some visible icon showing. I realized when i scrolled down to bottom and scroll back to the top, the one supposed to have that icon to be shown was not shown. I passed in an object into the custom view to tell which one to be displayed.
Item in listview, local:ChildInfoIconsView is the custom view.
<StackLayout Orientation="Vertical"
Grid.Row="0"
Grid.Column="1"
VerticalOptions="Center"
HorizontalOptions="Start">
<Label AutomationId="aChildName" Style="{StaticResource MediumBoldFont}" x:Name="childName" Text="{Binding DisplayName}" HorizontalOptions="StartAndExpand" />
<local:ChildInfoIconsView
Child="{Binding .}"
VerticalOptions="Fill">
</local:ChildInfoIconsView>
</StackLayout>
Main Code for custom view
public partial class ChildInfoIconsView : ContentView
{
public static readonly BindableProperty OrientationProperty = BindableProperty.Create(nameof(Orientation), typeof(StackOrientation), typeof(StackOrientation), StackOrientation.Vertical, BindingMode.OneWay, null);
public static readonly BindableProperty ShowClassroomProperty = BindableProperty.Create(nameof(ShowClassroom), typeof(bool), typeof(bool), true, BindingMode.OneWay, null);
public static readonly BindableProperty ChildProperty = BindableProperty.Create(nameof(Child), typeof(ChildModel), typeof(ChildModel), null, BindingMode.OneWay, null);
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName.Equals(nameof(Child)))
{
if (Child != null)
{
this.ImageUnauth.IsVisible = (Child.HasUnauthorized.HasValue) ? Child.HasUnauthorized.Value : false;
this.ImageMedical.IsVisible = (Child.HasMedical.HasValue) ? Child.HasMedical.Value : false;
this.ImageBirthday.IsVisible = Child.IsBirthday;
this.ImageNewChild.IsVisible = !string.IsNullOrEmpty(Child.NewChildFlagImage);
this.ImageClassroom.IsVisible = (!ShowClassroom) ? false : !string.IsNullOrEmpty(Child.Classroom);
this.textClass.Text = Child.Classroom;
this.textClass.IsVisible = (!ShowClassroom) ? false : !string.IsNullOrEmpty(Child.Classroom);
}
}
else if (propertyName.Equals(nameof(ShowClassroom)))
{
this.ImageClassroom.IsVisible = ShowClassroom;
this.textClass.IsVisible = ShowClassroom;
}
else if (propertyName.Equals(nameof(Orientation)))
{
this.layout.Orientation = Orientation;
}
}
public ChildInfoIconsView()
{
InitializeComponent();
}
public StackOrientation Orientation
{
get
{
return (StackOrientation)GetValue(OrientationProperty);
}
set
{
SetValue(OrientationProperty, value);
}
}
public bool ShowClassroom
{
get
{
return (bool)GetValue(ShowClassroomProperty);
}
set
{
SetValue(ShowClassroomProperty, value);
}
}
public ChildModel Child
{
get
{
return (ChildModel)GetValue(ChildProperty);
}
set
{
SetValue(ChildProperty, value);
}
}
It work fine when using old version of xamarin.forms. When updated the package, this happen on android.

Master/Details binding MVVM

I have some problem with correct binding my folder with images to master details and other operation with them.
So, I have model of folder and image
public class AppFolder
{
private long id;
private List<AppImage> appImages;
public AppFolder() { }
public List<AppImage> AppImages { get => appImages; set => appImages = value; }
public long Id { get => id; set => id = value; }
}
public class AppImage
{
private int id;
private string title;
private ImageSource appImageURL;
public AppImage() { }
public AppImage(string title, ImageSource imageSource)
{
Title = title;
AppImageURL = imageSource;
}
public int Id { get => id; set => id = value; }
public string Title { get => title; set => title = value; }
public ImageSource AppImageURL { get => appImageURL; set => appImageURL = value; }
}
And I bind List to Master/Details.
public class UserPhotosViewModel : ViewModelBase
{
private readonly IDataService dataService;
private readonly INavigationService navigationService;
public UserPhotosViewModel(IDataService dataService, INavigationService navigationService)
{
this.dataService = dataService;
this.navigationService = navigationService;
Initialize();
}
private async Task Initialize()
{
var item = new List<AppFolder>();
try
{
item = await dataService.GetDataList();
FolderList = item;
}
catch (Exception ex)
{
}
}
private List<AppFolder> folderList;
public List<AppFolder> FolderList
{
get { return folderList; }
set
{
folderList = value;
RaisePropertyChanged(nameof(FolderList));
}
}
}
Example xaml file
<controls:MasterDetailsView ItemsSource="{Binding FolderList}">
<controls:MasterDetailsView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Id}"></TextBlock>
</DataTemplate>
</controls:MasterDetailsView.ItemTemplate>
<controls:MasterDetailsView.DetailsTemplate>
<DataTemplate>
<controls:AdaptiveGridView ItemsSource="{Binding AppImages}"
OneRowModeEnabled="False"
ItemHeight="205"
DesiredWidth="205"
SelectionMode="Multiple"
Margin="0 6 0 0"
>
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate >
<Grid Background="White" Margin="10">
<Image
Source="{Binding AppImageURL}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
/>
<TextBlock Text="{Binding TextTitle}"></TextBlock>
</Grid>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
</DataTemplate>
</controls:MasterDetailsView.DetailsTemplate>
</controls:MasterDetailsView>
So, it's work correct and I saw my folders with images on page
enter image description here
Look nice and I think it all.
But when I want to add event and SelectedItem to AdaptiveGridView from MVVM model, I saw that it doesn't see them. Visual Studio show me that I could to write them in Model "AppFolder" but it's nonsens....
So, my question: How I can add event (binding command/method) to adaptive grid from UserPhotosViewModel?
Thank you for your time.
UPDATE
enter image description here
2) User double click on image and program send folder with this image and other to page and binding them to FlipView (imitation full screen viewer)
I try to add, but that way also offers to write event and property in model. I use this way for settings in navigation panel (parent control for master/details)
You just need to add command property in your AppFolder class like the following:
public class AppFolder:ViewModelBase
{
private long id;
private List<AppImage> appImages;
public AppFolder() { }
public List<AppImage> AppImages { get => appImages; set => appImages = value; }
public long Id { get => id; set => id = value; }
public RelayCommand<object> relayCommand { get; set; }
}
Then, in your UserPhotosViewModel, you could declare a method for initializing this command.
Since I do not know what the dataService.GetDataList() is. I just change this place in your code and make a simple code sample for you.
private void Initialize()
{
var item = new List<AppFolder>();
try
{
List<AppImage> ls = new List<AppImage>();
ls.Add(new AppImage() { Id = 1, Title = "aaa", AppImageURL = new BitmapImage(new Uri("ms-appx:///Assets/1.jpg")) });
ls.Add(new AppImage() { Id = 2, Title = "bbb", AppImageURL = new BitmapImage(new Uri("ms-appx:///Assets/2.jpg")) });
item.Add(new AppFolder() { Id = 1, AppImages = ls,relayCommand=new RelayCommand<object>(DoubleTapCommand) });
FolderList = item;
}
catch (Exception ex)
{
}
}
private void DoubleTapCommand(object obj)
{
//the obj will be an AppFolder object
}
<controls:MasterDetailsView.DetailsTemplate>
<DataTemplate>
<controls:AdaptiveGridView x:Name="grid" ItemsSource="{Binding AppImages}"
OneRowModeEnabled="False"
ItemHeight="205"
DesiredWidth="205"
SelectionMode="Multiple"
Margin="0 6 0 0">
<Interactivity:Interaction.Behaviors>
<Interactions:EventTriggerBehavior EventName="DoubleTapped">
<Interactions:InvokeCommandAction Command="{Binding relayCommand}" CommandParameter="{Binding}"></Interactions:InvokeCommandAction>
</Interactions:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate >
<Grid Background="White" Margin="10">
<Image
Source="{Binding AppImageURL}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
/>
<TextBlock Text="{Binding TextTitle}"></TextBlock>
</Grid>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
</DataTemplate>
</controls:MasterDetailsView.DetailsTemplate>
1) User select N images and delete them (example)
About this requirement, I suggested that you'd better add a button to bind command for deleting selected items.

How to change a radio box from true to false using MVVM

So I have a radio button that I want to change from being unchecked to being checked based on if a button is clicked. So
the XAML code I have is:
<RadioButton GroupName="rdoExchange" Grid.Row="2" Grid.Column="0" x:Name="PauseRadioButton" Command="{Binding PauseCommand}" IsChecked="{Binding Path=check, Mode=TwoWay}" Margin="3"/>
And the View Model Code:
public string check = "True";
public void Reset(object obj)
{
check = "True";
}
private ICommand m_PauseCommand;
public ICommand PauseCommand
{
get
{
return m_PauseCommand;
}
set
{
m_PauseCommand = value;
}
}
private ICommand m_ResetCommand;
public ICommand ResetCommand
{
get
{
return m_ResetCommand;
}
set
{
m_ResetCommand = value;
}
}
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
I've left out the relaycommand code and several other parts that I feel would be irrelevant to solving the problem.
Would something like this work?
XAML:
<RadioButton GroupName="rdoExchange" IsChecked="{Binding Path=OptionOneChecked, Mode=TwoWay}"/>
<Button Command="{Binding ToggleOptionOne}" Height="20" />
View Model:
public class ViewModel: NotificationObject
{
private bool _optionOneChecked;
public bool OptionOneChecked
{
get { return _optionOneChecked; }
set
{
if (value.Equals(_optionOneChecked)) return;
_optionOneChecked = value;
RaisePropertyChanged("OptionOneChecked");
}
}
public ICommand ToggleOptionOne
{
get { return new DelegateCommand(() => OptionOneChecked = !OptionOneChecked); }
}
}
using the NotificationObject class from the PRISM NuGet package.

RaisePropertyChanged doesn't work for ObservableCollection

I have a really weird problem with update UI using MVVM Light Toolkit. The RaisePropertyChanged doesn't work at all for my ObservableCollection.
Here is the XAML code:
<ListBox x:Name="list" ItemsSource="{Binding ModelList}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
<CheckBox IsChecked="{Binding IsChecked}"></CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<interaction:Interaction.Triggers>
<interaction:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding TempCommand}" CommandParameter="{Binding ElementName=list, Path=SelectedItem}"
PassEventArgsToCommand="True"/>
</interaction:EventTrigger>
</interaction:Interaction.Triggers>
</ListBox>
And there is ViewModel code part:
private Model _selectedItem;
public Model SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
RaisePropertyChanged("SelectedItem");
}
}
private ObservableCollection<Model> _modelList;
public ObservableCollection<Model> ModelList
{
get { return _modelList; }
set
{
_modelList = value;
RaisePropertyChanged("ModelList");
}
}
public RelayCommand<Model> TempCommand { get; private set; }
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
modelList = new ObservableCollection<Model>()
{
new Model()
{
IsChecked = true,
Name = "Temp1"
},
new Model()
{
IsChecked = false,
Name = "Temp2"
},
new Model()
{
IsChecked = false,
Name = "Temp3"
}
};
ModelList = modelList;
TempCommand = new RelayCommand<Model>(Model_SelectedItem);
}
private void Model_SelectedItem(Model item)
{
// What should I do here?
}
When I change the ModelList - there is no reaction from ListBox UI.
Anyone can help me ?
Problem solved.
When you have custom class in ObservableCollection or List it has to derived from ObservableObject and all the Properties have to fire RaisePropertyChanged event.