ObservableCollection not working as expected, mvvm light - c#

What I did is a search function to search for events.
My problem here is that there's a property that fires up when i enter the page and set my ObservableCollection count to 0, but there are events details in my database.
How this should have work is that when i enter on the page there is a relay command that executes and retrieve all data from database and place it into the observable collection and display the event names into a ListBox. It I only when I enter a character then after it detect the events.
Here are my codes:
xaml:
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Loaded">
<Core:InvokeCommandAction Command="{Binding Searchfromhomepage.EventSearch, Mode=OneWay}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<Grid x:Name="LayoutRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--<TextBox x:Name="txtSearch" Background="White" Text="{Binding Path=HomePage.TxtEntered, UpdateSourceTrigger=PropertyChanged}" FontSize="30" Height="57" Margin="19,10,19,0" Grid.Row="1" />-->
<TextBox x:Name="txtTest2" Text="{Binding Searchfromhomepage.Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.667,19,0">
<ListBox Background="Black" x:Name="listBox" FontSize="26" Margin="0,10,0,0" ItemsSource="{Binding Searchfromhomepage.FilteredNames, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="txtEventName" TextWrapping="Wrap" Text="{Binding EventName}" Tapped="txtEventName_Tapped" IsTapEnabled="True" Foreground="White" Width="300" Margin="10,15,0,0" Height="55"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
viewmodel codes:
private static ObservableCollection<Event> _searchEventCollection = new ObservableCollection<Event>();
public static ObservableCollection<Event> SearchEventCollection
{
get { return _searchEventCollection; }
set { _searchEventCollection = value; }
}
//search from homepage event section
private RelayCommand _eventSearch;
/// <summary>
/// Gets the EventSearch.
/// </summary>
public RelayCommand EventSearch
{
get
{ return _eventSearch
?? (_eventSearch = new RelayCommand(
async () =>
{
SearchEventCollection.Clear();
var eventList = await App.MobileService.GetTable<Event>().ToListAsync();
foreach (Event ename in eventList)
{
SearchEventCollection.Add(new Event
{
Id = ename.Id,
EventName = ename.EventName,
Date = ename.Date,
Location = ename.Location,
Desc = ename.Desc
});
}
}));
}
}
private string filter;
public String Filter
{
get
{
return this.filter;
}
set
{
this.filter = value;
RaisePropertyChanged("FilteredNames");
}
}
public List<Event> FilteredNames
{
get
{
if (filter == "")
{
return SearchEventCollection.ToList();
}
else
{
return (from name in SearchEventCollection where name.EventName.ToUpper().StartsWith(filter.ToUpper()) select name).ToList();
}
}
}
public searchfromhomepageViewModel()
{
filter = "";
}

in your view model code set up the _eventSearch command like so
public RelayCommand _eventSearch = new RelayCommand(EventSearch);

Related

WPF UniformGrid not showing

I'm developing a WPF application utilizing the MVVM design pattern, and I'd like to display a button matrix with the help of a UniformGrid with each gridcell being a colorable field.
I have a Grid having 4 rows set up inside my MainWindow.xaml.
The first row inside it holds a menu, the second contains a button.
A StackPanel is contained by the third Grid row with three buttons as children to the stackpanel. Their purpose is to mark which UniformGrid size will be in use.
I'd like to place the UniformGrid inside the fourth row in such a way that the third row of the main grid shall collapse just prior to the emergence of the buttonmatrix in order to ensure enough space required by the UiformGrid. However, I do not want the matrix to fill the whole window.
I'd like to modify the color of an individual matrixcell using an ObservableCollection with each element of the collection being a SolidColorBrush instance.
The problem is that if I click on one of the buttons of the StackPanel then the panel itself vanishes (as desired), however the buttonmatrix cannot be seen.
Any help is greatly appreciated!
View (MainWindow.xaml)
<Window x:Class="Game.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="GAME" Height="600" Width="800">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="visibilityConverter" />
</Window.Resources>
<Grid x:Name="mainGrid" Margin="0,0,0,171">
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition Height="22" />
<RowDefinition Height="{Binding GridThirdRowHeight}" />
<RowDefinition Height="500" />
</Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem Header="File">
<MenuItem Header="New game" Command="{Binding NewGameCommand}" />
<Separator />
<MenuItem Header="Load game" Command="{Binding LoadGameCommand}" />
<MenuItem Header="Save game" Command="{Binding SaveGameCommand}" />
<Separator />
<MenuItem Header="Exit" Command="{Binding ExitCommand}" />
</MenuItem>
</Menu>
<Button x:Name="buttonStartStopGame" Grid.Row="1" Background="Red" Foreground="White" Content="{Binding ButtonStartStopGameContent}" Height="22" Width ="66" HorizontalAlignment="Right" Command="{Binding ButtonStartStopGameClickCommand}"/>
<StackPanel x:Name="panel1" Grid.Row="2" Visibility = "{Binding IsPanelVisible, Converter={StaticResource visibilityConverter}}" Orientation="Vertical" Margin="124,112,191,110" Background="#66808080">
<Button x:Name="buttonSmallTable" Background="Red" Foreground="White" Content="15 X 15" Height="22" Width="66" HorizontalAlignment="Center" Margin="0,0,0,0" Command="{Binding ButtonSmallTableClickCommand}"/>
<Button x:Name="buttonMediumTable" Background="Red" Foreground="White" Content="17 X 17" Height="22" Width="66" HorizontalAlignment="Center" Margin="0,25,0,0" Command="{Binding ButtonMediumTableClickCommand}"/>
<Button x:Name="buttonLargeTable" Background="Red" Foreground="White" Content="19 X 19" Height="22" Width="66" HorizontalAlignment="Center" Margin="0,25,0,0" Command="{Binding ButtonLargeTableClickCommand}"/>
</StackPanel>
<ItemsControl Grid.Row="3" ItemsSource="{Binding Fields}">
<!--controller containing buttons-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!--we insert the UniformGrid here-->
<UniformGrid Rows="{Binding GameTableRowCount}" Columns="{Binding GameTableColCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<!--buttons being utilized as UniformGrid elements-->
<DataTemplate>
<Button Background="Green" Focusable="False" RenderTransformOrigin="0.5, 0.5">
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
ViewModel
private GameModel _model; // modell
private Boolean _isGameOn = false;
private Boolean _isPanelVisible = true;
private String _gridThirdRowHeight = "Auto";
private Int32 _gameTableRowCount;
private Int32 _gameTableColCount;
private String _buttonStartStopGameContent = "START";
#endregion
#region Properties
public String GridThirdRowHeight
{
get { return _gridThirdRowHeight; }
set { _gridThirdRowHeight = value;
OnPropertyChanged(nameof(GridThirdRowHeight)); }
}
public Boolean IsGameOn
{
get { return _isGameOn; }
set { _isGameOn = value; }
}
/// <summary>
/// Query and modification for the number of rows inside the gametable.
/// </summary>
public Int32 GameTableRowCount
{
get { return _gameTableRowCount; }
set
{
_gameTableRowCount = value;
OnPropertyChanged(nameof(GameTableRowCount));
}
}
/// <summary>
/// Query and modification for the number of columns inside the gametable.
/// </summary>
public Int32 GameTableColCount
{
get { return _gameTableColCount; }
set
{
_gameTableColCount = value;
OnPropertyChanged(nameof(GameTableColCount));
}
}
public Boolean IsPanelVisible
{
get { return _isPanelVisible; }
set
{
_isPanelVisible = value;
OnPropertyChanged(nameof(IsPanelVisible));
}
}
...
public DelegateCommand NewGameCommand { get; private set; }
public DelegateCommand SaveGameCommand { get; private set; }
public DelegateCommand LoadGameCommand { get; private set; }
public DelegateCommand ExitCommand { get; private set; }
public DelegateCommand ButtonSmallTableClickCommand { get; }
public DelegateCommand ButtonMediumTableClickCommand { get; }
public DelegateCommand ButtonLargeTableClickCommand { get; }
public DelegateCommand ButtonStartStopGameClickCommand { get; }
public ObservableCollection<SolidColorBrush> Fields { get; set; }
...
private void GenerateGameTable()
{
Fields = new ObservableCollection<SolidColorBrush>();
GridThirdRowHeight = "0";
GameTableRowCount = _model.GameTable.GetLength(0);
GameTableColCount = _model.GameTable.GetLength(1);
for (Int32 i = 0; i < _model.GameTable.GetLength(0); i++)
{
for (Int32 j = 0; j < _model.GameTable.GetLength(1); j++)
{
Fields.Add(new SolidColorBrush());
Fields[i * _model.GameTable.GetLength(1) + j] = Brushes.Green;
}
}
}
...
private async void ViewModel_ButtonSmallTableClick(object? sender, System.EventArgs e)
{
IsPanelVisible = false;
//here I generate the contents of the gametable
//for the model layer
GenerateGameTable(); // purpose: to create the visible matrix
}
}

How to get a scrollviewer's scrollable height to update in an interaction request

EDIT: I discovered that it was in fact the items presenter in my items control within the scroll viewer that wasn't updating correctly rather than the scrollviewer. I added an answer to reflect this.
I have a simple set up for a custom view interaction request. The view contains a scroll viewer but the scroll viewers scrollable height doesn't update if the items control within it has an items source update. The relevant code is below.
Confirmation model:
public class ProfileImportConfirmation : Confirmation
{
public ObservableCollection<ProfileAcceptPair> PossibleProfiles { get; set; } = new ObservableCollection<ProfileAcceptPair>();
public ObservableCollection<Profile> ConfirmedProfiles { get; set; } = new ObservableCollection<Profile>();
}
ViewModel:
public class ProfileImportPopupViewModel : BindableBase, IInteractionRequestAware
{
ProfileImportConfirmation _profileImportConfirmation;
public InteractionRequest<Confirmation> YesNoConfirmationInteractionRequest { get; }
public DelegateCommand AcceptCommand { get; set; }
public DelegateCommand CancelCommand { get; set; }
public ProfileImportPopupViewModel()
{
AcceptCommand = new DelegateCommand(Accept);
CancelCommand = new DelegateCommand(Cancel);
YesNoConfirmationInteractionRequest = new InteractionRequest<Confirmation>();
}
public INotification Notification
{
get { return _profileImportConfirmation; }
set
{
if (value is ProfileImportConfirmation confirmation)
{
_profileImportConfirmation = confirmation;
OnPropertyChanged(nameof(Notification));
}
}
}
public Action FinishInteraction { get; set; }
void Cancel()
{
_profileImportConfirmation.Confirmed = false;
FinishInteraction();
}
void Accept()
{
_profileImportConfirmation.Confirmed = true;
_profileImportConfirmation.ConfirmedProfiles.Clear();
_profileImportConfirmation.ConfirmedProfiles.AddRange(_profileImportConfirmation.PossibleProfiles.Where(p => p.Accepted).Select(p => p.Profile).ToList());
if (_profileImportConfirmation.ConfirmedProfiles.Any(p => p.IsRootProfile))
YesNoConfirmationInteractionRequest.Raise(
new Confirmation
{
Title = DisplayStrings.AreYouSureLabel,
Content = "Proceed?"
},
confirmed => FinishInteraction());
else
{
FinishInteraction();
}
}
}
View:
<UserControl
MaxHeight="500"
MinWidth="400"
d:DataContext="{d:DesignInstance Type=viewModels:ProfileImportPopupViewModel, IsDesignTimeCreatable=False}"
Loaded="ProfileImportPopup_OnLoaded">
<i:Interaction.Triggers>
<mvvm:InteractionRequestTrigger SourceObject="{Binding YesNoConfirmationInteractionRequest, Mode=OneWay}">
<mvvm:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" WindowStyle="{StaticResource PopupWindow}" WindowStartupLocation="CenterOwner">
<mvvm:PopupWindowAction.WindowContent>
<popups:YesNoConfirmationPopup />
</mvvm:PopupWindowAction.WindowContent>
</mvvm:PopupWindowAction>
</mvvm:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Grid Margin="30, 0, 30, 30">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.ColumnSpan="2" Content="{Binding Notification.Title}" HorizontalAlignment="Left" FontFamily="{StaticResource 'Brandon Grotesque Bold'}" FontSize="{StaticResource LargeFontSize}"/>
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Content="{Binding Notification.Content}" HorizontalAlignment="Center" VerticalContentAlignment="Center" FontFamily="{StaticResource 'Brandon Grotesque Bold'}" FontSize="{StaticResource LargeFontSize}"/>
<ScrollViewer x:Name="aoeu" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" CanContentScroll="True" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Notification.PossibleProfiles}" Margin="0, 0, 30, 0">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:ProfileAcceptPair}">
<CheckBox Style="{StaticResource RightAlignedCheckBox}" Content="{Binding Name}" IsChecked="{Binding Accepted}" HorizontalContentAlignment="Right"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Button Grid.Row="3" Grid.Column="0" HorizontalAlignment="Center" Content="{x:Static resources:DisplayStrings.CancelButton}" Style="{StaticResource ModalWindowButton}" Command="{Binding CancelCommand}" Margin="0" VerticalAlignment="Center"/>
<Button Grid.Row="3" Grid.Column="1" Content="{x:Static resources:DisplayStrings.OKButton}" Style="{StaticResource ModalWindowButton}" Command="{Binding AcceptCommand}" Margin="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
It seems the items source is updating fine and I can see the new item element hidden below the scroll viewer but I can't scroll down to it.
How can I get the scrollable height to update?
The problem wasn't with the scroll viewer. It was the items presenter from the items control inside the scroll viewer. It wasn't updating it's height on items changing.
My solution isn't ideal but it worked. I added a loaded event handler for the user control in the code behind. I then named the items control and using that found the items presenter child and called invalidate measure.
void Popup_OnLoaded(object sender, RoutedEventArgs e)
{
var itemsPresenter = (ItemsPresenter) FindChild(MyItemsControl, typeof(ItemsPresenter));
itemsPresenter.InvalidateMeasure();
}
public DependencyObject FindChild(DependencyObject o, Type childType)
{
DependencyObject foundChild = null;
if (o != null)
{
var childrenCount = VisualTreeHelper.GetChildrenCount(o);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(o, i);
if (child.GetType() != childType)
{
foundChild = FindChild(child, childType);
}
else
{
foundChild = child;
break;
}
}
}
return foundChild;
}

uwp gridview delete item with button within datatemplate

I have a gridview in UWP app and I have put a button in each gridview item in datatemplate so that it can be used to delete/remove that specific item from the gridview ( removing it from observableCollection behind). I am not using MVVM approach, because I am not much familiar with it, I am using a normal Observable Collection for binding of data and data template.
if you can suggest me a better way to do it, myabe using MVVM please suggest me how to do it. Thanks in advance
Code :
XAML GRID VIEW (button with the red back ground is the button I wanna use to delete item)
<controls:AdaptiveGridView Name="HistoryGridView" StretchContentForSingleRow="False"
Style="{StaticResource MainGridView}"
ItemClick ="HistoryGridView_SelectionChanged"
ItemsSource="{x:Bind HistoryVideos, Mode=OneWay}">
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate x:DataType="data:Video">
<StackPanel Margin="4" >
<Grid>
<Button Background="Red"
HorizontalAlignment="Right" VerticalAlignment="Top"
Height="36" Canvas.ZIndex="1"
Style="{StaticResource TransparentButton}" Width="36">
<fa:FontAwesome Icon="Close" FontSize="20" HorizontalAlignment="Center" Foreground="White"
/>
</Button>
<Image Canvas.ZIndex="0" Source="{x:Bind Thumbnail}" Style="{StaticResource GridViewImage}"/>
<Border Style="{StaticResource TimeBorder}" Height="Auto" VerticalAlignment="Bottom"
Canvas.ZIndex="1"
HorizontalAlignment="Left">
<TextBlock Text="{x:Bind Duration}" Foreground="White" Height="Auto"/>
</Border>
</Grid>
<TextBlock Text="{x:Bind Name}" Style="{StaticResource GridViewVideoName}"/>
<TextBlock Text="{x:Bind ParentName}" Style="{StaticResource GridViewParentName}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{x:Bind Views}" Style="{StaticResource GridViewViews}"/>
<TextBlock Text="Views" HorizontalAlignment="Right"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
Code Behind
public History()
{
this.InitializeComponent();
HistoryVideos = new ObservableCollection<Video>();
}
public ObservableCollection<Video> HistoryVideos { get; private set; }
I am using onnavigated to method for filling the collection and it works fine and also I guess that is not relevent here.
We can add the Command to the Button to invoke when this button is pressed and we can use parameter to pass to the Command property.
To use the Command, we should be able to define a DelegateCommand class that inherits from the ICommand.
For example:
internal class DelegateCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public DelegateCommand(Action<object> execute)
{
this.execute = execute;
this.canExecute = (x) => { return true; };
}
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
public void Execute(object parameter)
{
execute(parameter);
}
}
We can add the Id property in the Video, then we can pass the Id property to the CommandParameter. When we click the Button, the ExecuteDeleteCommand method will be fired. We can use the Id to find the Video in the HistoryVideos and use the Remove method to remove it.
The ViewModel code:
internal class ViewModel
{
private ObservableCollection<Viedo> _videos;
public ObservableCollection<Viedo> Videos
{
get
{
return _videos;
}
set
{
if (_videos != value)
{
_videos = value;
}
}
}
public ICommand DeleteCommand { set; get; }
private void ExecuteDeleteCommand(object param)
{
int id = (Int32)param;
Viedo cus = GetCustomerById(id);
if (cus != null)
{
Videos.Remove(cus);
}
}
private Viedo GetCustomerById(int id)
{
try
{
return Videos.First(x => x.Id == id);
}
catch
{
return null;
}
}
public ViewModel()
{
Videos = new ObservableCollection<Viedo>();
for (int i = 0; i < 5; i++)
{
Videos.Add(new Viedo());
Videos[i].Name = "Name";
Videos[i].Id = i;
}
this.DeleteCommand = new DelegateCommand(ExecuteDeleteCommand);
}
}
The XAML code:
<GridView Name="MyGridView" ItemsSource="{Binding Videos}">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"></TextBlock>
<Button Background="Red"
HorizontalAlignment="Right" VerticalAlignment="Top"
Height="36" Canvas.ZIndex="1"
Width="36" Command="{Binding DataContext.DeleteCommand, ElementName=MyGridView}" CommandParameter="{Binding Id}">
</Button>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The code behind:
private ViewModel myViewModel;
public MainPage()
{
this.InitializeComponent();
myViewModel = new ViewModel();
MyGridView.DataContext = myViewModel;
}
Update:
<GridView Name="MyGridView" ItemsSource="{x:Bind myViewModel.Videos}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:Viedo">
<StackPanel>
<TextBlock Text="{x:Bind Name}"></TextBlock>
<Button Background="Red"
HorizontalAlignment="Right" VerticalAlignment="Top"
Height="36" Canvas.ZIndex="1"
Width="36" Command="{Binding DataContext.DeleteCommand, ElementName=MyGridView}" CommandParameter="{Binding Id}">
</Button>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>

Binding across tab items with button

I have a wpf with multiple tabs. The application is a step by step learning tool for students. I want the tabs to be locked initially. When the user has entered the correct data in a tab, the next is enabled after pressing the proceed button.
I tried binding the 'isEnabled' property of tabitem and setting it true when the button is pressed in the View model. It doesn't work.
Here's some snippets of my code:
XAML:
<TabItem Header="Step 1"><my:Step1 Loaded="Step1_Loaded" /></TabItem>
<TabItem Header="Step 2"><my:Step2 Loaded="Step2_Loaded" /></TabItem>
<TabItem IsEnabled="{Binding step3Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Step 3"><my:Step3 Loaded="Step3_Loaded" /></TabItem>
Code-behind for the button:
private void proceed2_Click(object sender, RoutedEventArgs e)
{
var vm = DataContext as ViewModel.ValuesCheck;
if (vm != null)
vm.Step2check();
}
View Model:
class ViewModel:INotifyPropertyChanged
{
bool _step3enabled = false;
public bool step3Enabled
{
get { return _step3enabled; }
set { _step3enabled = value; OnPropertyChanged("step3Enabled"); }
}
public void Step2check()
{
this.step3Enabled = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
}
As I can understand, you need some navigation control that presents its content based on some condition. I can suggest you to use a listbox control and manage its ListBoxItems content visibility or something like this.
Xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding Controls}" SelectedItem="{Binding CurrentControlContent}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Visibility="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource Converter}}"
BorderBrush="Tomato" BorderThickness="1" IsEnabled="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<Button Width="100" Height="30" IsEnabled="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
IsHitTestVisible="False" Content="{Binding Path = Content, UpdateSourceTrigger=PropertyChanged}"></Button>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ContentControl Content="{Binding CurrentControlContent.Content}"></ContentControl>
<Button Grid.Row="1" Content="Check" Command="{Binding CheckCommand}"></Button>
</Grid>
ViewModel
private TabAndControlModel _selectedTabControlModel;
private ICommand _checkCommand;
private TabAndControlModel _currentControlContent;
public ObservableCollection<TabAndControlModel> Controls { get; set; }
public TabAndControlModel CurrentControlContent
{
get { return _currentControlContent; }
set
{
_currentControlContent = value;
OnPropertyChanged();
}
}
public ICommand CheckCommand
{
get { return _checkCommand ?? (_checkCommand = new RelayCommand(Check)); }
}
private void Check()
{
var index = Controls.IndexOf(CurrentControlContent);
var nextIndex = index + 1;
if(nextIndex >= Controls.Count) return;
CurrentControlContent = Controls[nextIndex];
CurrentControlContent.TabIsEnabled = true;
}
Model
private bool _tabIsEnabled;
private string _content;
public bool TabIsEnabled
{
get { return _tabIsEnabled; }
set
{
_tabIsEnabled = value;
OnPropertyChanged();
}
}
public string Content
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged();
}
}
View on running:
And you have to template and style all that stuff...
Regards

WPF - MVVM : How to Check/Uncheck all Items in a ListView

I have the following requirements:
Window will show a ListView with multiple items.
User should be able to check (Checkbox) any item.
a) If one item, all items should be unchecked and disabled.
b) If checked item is unchecked, than all items should be enabled.
As of now, I have the following incomplete code.
MainWindow XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="520.149" Width="732.463">
<Window.Resources>
<ResourceDictionary Source="MainWindowResource.xaml" />
</Window.Resources>
<Grid>
<ListView x:Name="myListBox" ItemTemplate="{StaticResource OfferingTemplate}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
</Window>
DataTemplete for ListView:
<DataTemplate x:Key="OfferingTemplate">
<StackPanel>
<Grid IsEnabled="{Binding IsEnabled}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"></ColumnDefinition>
<ColumnDefinition Width="120"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Rectangle Grid.Column="0" Grid.RowSpan="3" Fill="#F4CA16" />
<Label
Grid.Column="1"
Grid.Row="0"
Content="{Binding Title}"
FontSize="18" FontWeight="Bold"
Margin="0,0,0,0" />
<TextBlock
Grid.Column="1"
Grid.Row="1"
FontSize="10"
Text="{Binding Description}"
Foreground="Black"
TextWrapping="WrapWithOverflow"
Margin="5,0,0,0" />
<CheckBox
Grid.Column="1"
Grid.Row="2"
FontSize="14"
IsChecked="{Binding IsSelected}"
VerticalAlignment="Bottom"
Margin="5,0,0,0">
<TextBlock Text="Select" Margin="0,-2,0,0"/>
</CheckBox>
</Grid>
</StackPanel>
</DataTemplate>
Model:
class MyModel
{
public string Title { get; set; }
public string Description { get; set; }
public bool IsSelected { get; set; }
public bool IsEnabled { get; set; }
}
ViewModel:
class MyViewModel : INotifyPropertyChanged
{
private MyModel offering;
public MyViewModel()
{
offering = new MyModel();
}
public int ID { get; set; }
public string Title
{
get { return offering.Title; }
set
{
offering.Title = value;
RaisePropertyChanged("Title");
}
}
public string Description
{
get { return offering.Description; }
set
{
offering.Description = value;
RaisePropertyChanged("Description");
}
}
public bool IsSelected
{
get { return offering.IsSelected; }
set
{
offering.IsSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public bool IsEnabled
{
get { return offering.IsEnabled; }
set
{
offering.IsEnabled = value;
RaisePropertyChanged("IsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This is an interesting question. Since the action you want applies to all items in the list, this logic should in list class level. Your MyViewModel class is fine. You need add some logic in your list class and XAML but thanks to Prism, it is quite easy.
The list class (not shown in your post) Contains:
public ObservableCollection<MyViewModel> MyItems { get; set; } //Binding to ItemsSource
private ICommand _selectCommand;
public ICommand SelectCommand
{
get { return _selectCommand ?? (_selectCommand = new DelegateCommand<MyViewModel>(DoSelect)); }
}
private void DoSelect(MyViewModel myViewModel)
{
foreach(var item in MyItems)
if (item != myViewModel)
{
item.IsSelected = false;
item.IsEnabled = false;
}
}
private ICommand _unselectCommand;
public ICommand UnselectCommand
{
get { return _unselectCommand ?? (_unselectCommand = new DelegateCommand<MyViewModel>(DoUnselect)); }
}
private void DoUnselect(MyViewModel myViewModel)
{
foreach (var item in MyItems)
if (item != myViewModel)
{
item.IsEnabled = true;
}
}
There are two commands, one for selecting and the other for unselecting. The magic is on XAML:
<ListView ItemsSource="{Binding Path=MyItems}" x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected}" IsEnabled="{Binding Path=IsEnabled}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.SelectCommand}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.UnselectCommand}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Using Prism's triggers, you can map CheckBox's Checked and Unchecked event to your list view model's commands and passing the item view model as parameter.
It is working perfectly but one thing is annoying, that setting item's IsSelected is separate. When you check a CheckBox, the item behind is set to true through DataBinding but all others are set through parent view model. If your post is all your requirement, you can remove IsChecked binding and put the logic of setting one IsSelected inside list view model, which looks clenaer and easier to write test code.

Categories

Resources