so I've been working on a smart home implementation in c# for quite a while now but I can't figure out how to inform the Command which executes something, in this case just a remove button as an example, about the item of the Observable that triggers it.
To implement the Collection I'm using a template which also contains a button so that each item of the Collection has a button by it's own.
ObservableCollection with remove Button for each Item
So as you see in there, I have a Remove Button for each Item that is in the ObservableCollection and if I press it I want the Item which contains the Button to be removed but without using something like selectedItem because in that case I need to selected the item first before using the button which is not what I really want because you can press the button without selecting the item/row.
In later time the button will be exchanged for a different function which needs to know which item the button belongs to.
xaml code:
<Page x:Class="Smart_Home.switchPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Smart_Home"
mc:Ignorable="d"
Title="switchPage" Height="490" Width="998" Background="#10636e" >
<Page.Resources>
<local:SwitchViewmodel x:Key="SwitchViewmodel" />
<DataTemplate x:Key="UserTemplate">
<StackPanel>
<Grid>
<Grid Width="970" Margin="0,0,0,0">
<Grid HorizontalAlignment="Left" VerticalAlignment="Center" Width="300">
<TextBlock Text="{Binding Path=schalterName}"/>
</Grid>
<Grid HorizontalAlignment="Center" Width="380">
<Grid HorizontalAlignment="Left" VerticalAlignment="Center" Width="200">
<TextBlock Text="{Binding Path=schalterRaum}"/>
</Grid>
<Grid HorizontalAlignment="Right" VerticalAlignment="Center" Width="30">
<TextBlock Text="{Binding Path=schalterNummer}"/>
</Grid>
</Grid>
<Grid HorizontalAlignment="Right" VerticalAlignment="Center" Width="60">
<Button Content="Switch" Height="40" Command="{Binding RemoveSelectedSwitch, Source={StaticResource SwitchViewmodel}}" />
</Grid>
</Grid>
</Grid>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid x:Name="WindowSelection" HorizontalAlignment="Left" Height="490" Margin="0,0,0,0" VerticalAlignment="Top" Width="998">
<Grid Height="400" VerticalAlignment="Top" Grid.ColumnSpan="2">
<Grid x:Name="ListNameGrid" VerticalAlignment="Top" Height="30">
<Grid HorizontalAlignment="Left" Margin="10,0,0,0">
<Label Content="Name" FontSize="14" Foreground="White"/>
</Grid>
<Grid HorizontalAlignment="Center" Width="400">
<Grid HorizontalAlignment="Left">
<Label Content="Room" FontSize="14" Foreground="White"/>
</Grid>
<Grid HorizontalAlignment="Right">
<Label Content="Number" FontSize="14" Foreground="White"/>
</Grid>
</Grid>
<Grid HorizontalAlignment="Right" Margin="0,0,10,0">
<Label Content="Status" FontSize="14" Foreground="White" />
</Grid>
</Grid>
<Grid x:Name="ListGrid" VerticalAlignment="Top" Margin="0,30,0,0">
<ListBox x:Name="Switches_lb" IsSynchronizedWithCurrentItem="true" FontSize="20" Foreground="White" ItemsSource="{Binding SwitchesList, Source={StaticResource SwitchViewmodel}}" ItemTemplate="{StaticResource UserTemplate}" Background="#10636e" BorderThickness="0" Margin="5,0,5,0"/>
</Grid>
</Grid>
</Grid>
</Page>
Viewmodel:
public class SwitchViewmodel
{
public int selectedCollection { get; }
public ObservableCollection<SwitchView> SwitchesList { get; set; }
public ICommand AddNewSwitch { get; set; }
public ICommand RemoveSelectedSwitch { get; set; }
public SwitchViewmodel()
{
AddNewSwitch = new Command(AddNewSwitchExecuteMethod, AddNewSwitchcanexecuteMethod);
RemoveSelectedSwitch = new Command(RemoveSelectedSwitchExeCuteMethod, RemoveSelectedSwitchAddNewSwitchcanexecuteMethod);
SwitchesList = new ObservableCollection<SwitchView>
{
new SwitchView() { schalterName = "Wandlampe", schalterRaum = "Wohnzimmer", schalterNummer = 1, schalterStatus = false },
new SwitchView() { schalterName = "Deckenlampe", schalterRaum = "Küche", schalterNummer = 2, schalterStatus = false },
new SwitchView() { schalterName = "Tischlampe", schalterRaum = "Wohnzimmer", schalterNummer = 39, schalterStatus = false },
};
}
private bool RemoveSelectedSwitchAddNewSwitchcanexecuteMethod(object parameter)
{
return true;
}
private void RemoveSelectedSwitchExeCuteMethod(object parameter)
{
//In here remove method
}
private bool AddNewSwitchcanexecuteMethod(object parameter)
{
return true;
}
private void AddNewSwitchExecuteMethod(object parameter)
{
Console.WriteLine("Command test");
Console.WriteLine("Count vorher: " + SwitchesList.Count());
SwitchesList.Add(new SwitchView() { schalterName = "Wandlampe", schalterRaum = "Wohnzimmer", schalterNummer = 1, schalterStatus = false });
Console.WriteLine("Count nachher: " + SwitchesList.Count());
}
}
Collection:
public class SwitchView
{
public string schalterName { get; set; }
public string schalterRaum { get; set; }
public int schalterNummer { get; set; }
public Boolean schalterStatus { get; set; }
}
Command:
public class Command : ICommand
{
Action<object> executeMethod;
Func<object, bool> canexecuteMethod;
public Command(Action<object> executeMethod, Func<object, bool> canexecuteMethod)
{
this.executeMethod = executeMethod;
this.canexecuteMethod = canexecuteMethod;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
executeMethod(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
So it would be really helpful if someone could help me out with my problem as I'm really new in using MVVM pattern in c# wpf.
Thank you and have a nice day
add CommandParameter.
<Button Content="Switch" Height="40"
Command="{Binding RemoveSelectedSwitch, Source={StaticResource SwitchViewmodel}}"
CommandParameter="{Binding}" />
and use received parameter in command method.
private void RemoveSelectedSwitchExeCuteMethod(object parameter)
{
var item = parameter as SwitchView;
}
since Button is inside ListBox ItemTemplate, CommandParameter="{Binding}" will return DataContext of ListBoxItem - which means corresponding data item
Related
So here I have a MVVM form. the Form contains a Datagrid which is connected to the Databank. I also have a ComboBox which I want to use as a filter option. The Filter option shoud filter by the "AnlV nr" so when the user selects "01" from the ComboBox the datagrid should refresh and show only that "AnlV nr" that have "01" Below I will share you the code and you can see that i've gotten as far as showing the "AnlV" values in the ComboBox but I now do not know how to do the rest and make the filter work. Below is my Viewmodel and the Xaml code.
If anyone can help me with this I would really apreciate it.
Xaml Code:
<Window x:Class="QBondsFrontend.Views.Input.AnlVTexteView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:QBondsFrontend.Views.Input" xmlns:input="clr-namespace:QBondsFrontend.ViewModels.Input" d:DataContext="{d:DesignInstance Type=input:AnlVTexteViewModel}"
mc:Ignorable="d"
Title="AnlVTexteView"
Width="800"
MinHeight="400"
Height="490"
MinWidth="1010"
MaxWidth="1010"
UseLayoutRounding="True">
<Grid Background="#A8A8A8" >
<StackPanel VerticalAlignment="Top" Background="#A8A8A8" Orientation="Horizontal" Height="57">
<Label
Content="AnlV Nr.:" Height="35" FontSize="12"/>
<ComboBox Background="LightGray" Height="20" Width="70" ItemsSource="{Binding lstAnlVTexte}" SelectedItem="{Binding Search}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding AnlVPara}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Height="18" Width="68" Margin="5, 0"
Content="Filter löschen" FontSize="11" Style="{StaticResource STL_ButtonStandard}"
x:Name="filterlöschen"
Command="{Binding icdFilterDelete}"/>
</StackPanel>
<StackPanel Background="LightGray" VerticalAlignment="Top" Height="177" Margin="0,57,0,0">
<DataGrid x:Name="datagridXAML"
Height="177"
ItemsSource="{Binding lstAnlVTexte, Mode=TwoWay}"
Style="{StaticResource STL_DataGridReporting}"
CellStyle="{StaticResource STL_DataGridCellReporting}"
ColumnHeaderStyle="{StaticResource STL_DataGridColumnHeaderReporting}"
AlternatingRowBackground="#A8A8A8"
CanUserResizeColumns="False"
>
<DataGrid.Columns>
<DataGridTextColumn Header="AnlV-Nr"
Binding="{Binding AnlVPara}"
Width="60"/>
<DataGridTextColumn Header="gültig ab"
Binding="{Binding TextGueltigAb}"
Width="68"/>
<DataGridTextColumn Header="Text"
Binding="{Binding ParaText}"
Width="750"/>
<DataGridTextColumn Header="Info"
Binding="{Binding Info}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<StackPanel Background="#A8A8A8" HorizontalAlignment="Center" Margin="10,268,0,141" Width="1010" >
<Label Content="Bearbeitungsbereich" FontWeight="Bold" FontSize="12" Height="33" />
</StackPanel>
<StackPanel>
<StackPanel Orientation="Horizontal" Background="#A8A8A8" HorizontalAlignment="Center"
Width="1010" Margin="0,294,0,0" Height="31">
<Label Height="25" Width="60" Margin="20, 0, 0, 0" Content="AnlV-Nr.:" />
<ComboBox IsEditable="True" Background="gray" Height="22" Width="69" ItemsSource="{Binding AnlVPara}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding lstAnlVTexte}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox Height="15" Margin="10, 0, 0, 0" />
<Label Height="26" Width="122" Content="Editierwarnungen" />
<StackPanel Height="48" Width="100"/>
</StackPanel>
<StackPanel Height="22" Orientation="Horizontal">
<Label Margin="20, 0, 0, 0" Content="gültig ab:" Height="27" />
<TextBox Background="LightGray" Height="20" Width="100" />
</StackPanel>
<StackPanel Height="50" Orientation="Horizontal">
<Label Content="Text:" Height="27" Width="38" Margin="42,0,0,10" />
<TextBox Background="LightGray" Width="500" Height="43" />
</StackPanel>
<StackPanel Orientation="Horizontal" >
<Label Content="Info:" Height="27" Width="38" Margin="42,0,0,0" />
<TextBox Background="LightGray" Width="500" Height="20" />
<Button x:Name="BTN_speichern" Width="80" Height="18" Margin="20,0,0,0" Content="Speichern"
Style="{StaticResource STL_ButtonStandard}" Command="{Binding icdSpeichern}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
ViewModel:
using Newtonsoft.Json;
using QBondsData.DBModels;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Linq;
namespace QBondsFrontend.ViewModels.Input
{
public class AnlVTexteViewModel : BaseViewModel
{
#region data
private string _AnlVPara;
private DateTime _TextGueltigAb;
private string _ParaText;
private string _Info;
private List<AnlVhistText> _lstAnlVTexte;
private string _search;
#endregion
#region constructor
public AnlVTexteViewModel()
{
icdFilterDelete = new RelayCommand<object>(parameter => filterdelete(), parameter => true);
icdSpeichern = new RelayCommand<object>(parameter => speichern(), parameter => true);
GetData();
}
#endregion
#region members
public ICommand icdFilterDelete { get; set; }
public ICommand icdSpeichern { get; set; }
private string Search
{
get { return _search; }
set
{
_search = value;
OnPropertyChanged("Search");
}
}
public string AnlVPara
{
get
{
return _AnlVPara;
}
set
{
_AnlVPara = value;
OnPropertyChanged("AnlVPara");
}
}
public DateTime TextGueltigAb
{
get
{
return _TextGueltigAb;
}
set
{
_TextGueltigAb = value;
OnPropertyChanged("TextGueltigAb");
}
}
public string ParaText
{
get
{
return _ParaText;
}
set
{
_ParaText = value;
OnPropertyChanged("ParaText");
}
}
public string Info
{
get
{
return _Info;
}
set
{
_Info = value;
OnPropertyChanged("Info");
}
}
public List<AnlVhistText> lstAnlVTexte
{
get { return _lstAnlVTexte; }
set
{
_lstAnlVTexte = value;
OnPropertyChanged("lstAnlVTexte");
}
}
#endregion
#region methods
private void filterdelete()
{
}
private void speichern()
{
}
private async Task GetData()
{
HttpResponseMessage Response = await Globals.SendRequest("AnlVTexte/GetAnlVTexte"
, "GET");
if (Response.IsSuccessStatusCode)
{
lstAnlVTexte = JsonConvert.DeserializeObject<List<AnlVhistText>>
(await Response.Content.ReadAsStringAsync());
}
else
{
lstAnlVTexte = new List<AnlVhistText>();
Application.Current.Dispatcher.Invoke((Action)delegate
{
Globals.CloseReportByHash(this.GetHashCode()
, "Fehler! (HTTP Status " + Response.StatusCode + ")." +
"Kontaktieren Sie den Support.");
});
}
}
#endregion
}
}
When you change the type of lstAnlVTexte to ICollectionView you get two events CurrentChanged and CurrentChanging which you can handle in your viewmodel. From the ICollectionView you can get the CurrentItem.
Like this:
private List<AnlVhistText> _anlVTexte = new List<AnlVhistText>();
public AnlVTexteViewModel()
{
[...]
lstAnlVTexte = new CollectionView(_anlVTexte);
lstAnlVTexte.CurrentChanged += SelectionChanged; // raised after the current item has been changed.
lstAnlVTexte.CurrentChanging += SelectionChanging; // raise before changing the current item. Event handler can cancel this event.
}
private void SelectionChanged(object sender, EventArgs e)
{
var selectedItem = lstAnlVTexte.CurrentItem;
}
private void SelectionChanging(object sender, CurrentChangingEventArgs e)
{
}
private ICollectionView _lstAnlVTexte;
public ICollectionView lstAnlVTexte
{
get { return _lstAnlVTexte; }
set
{
_lstAnlVTexte = value;
OnPropertyChanged("lstAnlVTexte");
}
}
Here's a sample using the community toolkit mvvm and linq.
If you're not familiar, the toolkit does code generation.
This is a simple scenario to illustrate the approach.
Mainwindowviewmodel.
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private int selectedFilterInt = -1;
partial void OnSelectedFilterIntChanged(int newValue)
{
FilteredList = new ObservableCollection<MyItem>(FullList.Where(x=>x.Category == selectedFilterInt).ToList());
}
public List<int> FilterOptions { get; set; } = new List<int> {1,2,3};
private List<MyItem> FullList= new List<MyItem>
{
new MyItem{ Category = 1},
new MyItem{ Category = 1},
new MyItem { Category = 1 },
new MyItem { Category = 2 },
new MyItem { Category = 2 },
new MyItem { Category = 3 }
};
[ObservableProperty]
private ObservableCollection<MyItem> filteredList = new ObservableCollection<MyItem>();
public MainWindowViewModel()
{
FilteredList = new ObservableCollection<MyItem>(FullList);
}
}
There's a full list of all the items.
But a filtered list is going to be bound to the itemssource of my listbox ( equivalent to your datagrid ).
Due to the code generated, when selectedFilterInt changes, OnSelectedFilterIntChanged will be called. It's got a handler listening for property changed of SelectedFilterInt if you dug into the generated code.
That method uses a linq where to filter the full list into filtered list.
Setting that filtered list property raises property changed and the view re reads the new collection.
MainWindow. ( I did mention this is simplified )
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<ComboBox SelectedItem="{Binding SelectedFilterInt}"
ItemsSource="{Binding FilterOptions}"/>
<ListBox ItemsSource="{Binding FilteredList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Category}"/>
<TextBlock Text="{Binding Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
and MyItem
public partial class MyItem : ObservableObject
{
[ObservableProperty]
private int category = 0;
[ObservableProperty]
private string comment = "Some test string";
}
Which is a bit underwhelming visually but works:
In your code you need to get all the data into a collection.
Call that FulList.
You then need another collection which will be the filtered data.
Call this FilteredList.
Bind itemssource to FilteredList
Initially, you presumably want FilteredList to be = FullList
Then when the user selects something in the combobox you need to react to that.
You could bind selecteditem to a property and act in the setter or handle propertychanged like my code does.
However you do it, you get a new integer.
You then use that to filter FullList into a new collection which will replace the bound FilteredList.
You also need to somehow have one entry per AnlV nr whatever that is in your combobox.
AnlV nr isn't going to work as a property name since it's got a space but it is the equivalent to Category in my sample.
You will use that selected value in the linq.
Substitute the name of that property for Category. Substitute the type of whatever your collection is. Maybe that's AnlVhistText. I'm not sure.
How can I make the dropdown sub-items clickable? i.e if I click on 'Employees' it will open the Employees page. I copied this from a tutorial however they didn't explain how to make click events with this creation.
Subitem Class
public class SubItem
{
public SubItem(string name, UserControl screen = null)
{
Name = name;
Screen = screen;
}
public string Name { get; private set; }
public UserControl Screen { get; private set; }
}
}
ItemMenu Class
public class ItemMenu
{
public ItemMenu(string header, List subItems, PackIconKind icon)
{
Header = header;
SubItems = subItems;
Icon = icon;
}
public ItemMenu(string header, UserControl screen, PackIconKind icon)
{
Header = header;
Screen = screen;
Icon = icon;
}
public string Header { get; private set; }
public PackIconKind Icon { get; private set; }
public List<SubItem> SubItems { get; private set; }
public UserControl Screen { get; private set; }
}
}
**UserControlMenuItem.xaml**
<Grid>
<materialDesign:PackIcon Kind="{Binding Path=Icon}" Width="15" Height="15" Margin="10 16" Foreground="White"/>
<ListBoxItem x:Name="ListViewItemMenu" Content="{Binding Path=Header}" Padding="37 14" FontSize="15" Foreground="White"/>
<Expander x:Name="ExpanderMenu" Header="{Binding Path=Header}" IsExpanded="False" Width="210" HorizontalAlignment="Right" Background="{x:Null}" Foreground="White">
<ListView x:Name="ListViewMenu" ItemsSource="{Binding Path=SubItems}" Foreground="White" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" Padding="20 5"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Expander>
</Grid>
**MainWindow.xaml**
<materialDesign:ColorZone Mode="PrimaryMid" Grid.ColumnSpan="2" HorizontalAlignment="Stretch">
<Grid>
<materialDesign:PopupBox PlacementMode="BottomAndAlignRightEdges" HorizontalAlignment="Right" Margin="10"/>
</Grid>
</materialDesign:ColorZone>
<Grid HorizontalAlignment="Stretch" Grid.Row="1" Background="{StaticResource PrimaryHueMidBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="326*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="GhostWhite">
<Image Source="Images/logo.png"/>
</Grid>
<ScrollViewer HorizontalAlignment="Stretch" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Grid.Row="1">
<StackPanel x:Name="Menu" Margin="10" />
</ScrollViewer>
</Grid>
<Frame Source="/CSA;component/Pages/Landing.xaml" Grid.Row="1" x:Name="ContentFrame" Grid.Column="1" Grid.ColumnSpan="2" Grid.RowSpan="2" Background="#2C2F33" Opacity="0.85" NavigationUIVisibility="Hidden" />
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var menuRegister = new List<SubItem>();
menuRegister.Add(new SubItem("Customer"));
menuRegister.Add(new SubItem("Providers"));
menuRegister.Add(new SubItem("Employees"));
menuRegister.Add(new SubItem("Products"));
var item6 = new ItemMenu("Register", menuRegister, PackIconKind.Register);
var menuSchedule = new List<SubItem>();
menuSchedule.Add(new SubItem("Services"));
menuSchedule.Add(new SubItem("Meetings"));
var item1 = new ItemMenu("Appointments", menuSchedule, PackIconKind.Schedule);
var menuReports = new List<SubItem>();
menuReports.Add(new SubItem("Customers"));
menuReports.Add(new SubItem("Providers"));
menuReports.Add(new SubItem("Products"));
menuReports.Add(new SubItem("Stock"));
menuReports.Add(new SubItem("Sales"));
var item2 = new ItemMenu("Reports", menuReports, PackIconKind.FileReport);
var menuExpenses = new List<SubItem>();
menuExpenses.Add(new SubItem("Fixed"));
menuExpenses.Add(new SubItem("Variable"));
var item3 = new ItemMenu("Expenses", menuExpenses, PackIconKind.ShoppingBasket);
var menuFinancial = new List<SubItem>();
menuFinancial.Add(new SubItem("Cash flow"));
var item4 = new ItemMenu("Financial", menuFinancial, PackIconKind.ScaleBalance);
var item0 = new ItemMenu("Dashboard", new UserControl(), PackIconKind.ViewDashboard);
Menu.Children.Add(new UserControlMenuItem(item0));
Menu.Children.Add(new UserControlMenuItem(item6));
Menu.Children.Add(new UserControlMenuItem(item1));
Menu.Children.Add(new UserControlMenuItem(item2));
Menu.Children.Add(new UserControlMenuItem(item3));
Menu.Children.Add(new UserControlMenuItem(item4));
}
```
All you need is to make the thing in ListView's ItemTemplate clickable. The easiest way to do that is to use a Button instead of a TextBlock. You can disable the Button's background and border to make it appear like text, but remain clickable.
Also, you don't need to use a ListView within an Expander. ListView offers scrolling capabilities and you don't need that. An ItemsControl will be enough.
Here's an example:
<Expander
Header="Expand me">
<ItemsControl ItemsSource="{Binding SubItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Background="Transparent"
BorderThickness="0"
Command="{Binding DataContext.DoSomethingCommand, ElementName=ThisWindow}"
Content="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
I'm binding to the same ICommand for all the sub items in this example. Realisticly, you would either pass a CommandParameter as well for the different sub items or bind to a different command per item.
After hours of searching the web I found a solution.
Method
public static void Navigate(object target)
{
((MainWindow)Application.Current.Windows[0]).ContentFrame.Content = target;
}
Implementation
switch (Globals.PageName)
{
case "Landing":
Pages.Landing itemZ = new Pages.Landing();
Navigate(itemZ);
break;
case "ClientAcquisition":
Pages.ClientAcquisition item = new Pages.ClientAcquisition();
Navigate(item);
break;
I want to have a Delete button for every element in my ListView, I searched stackoverflow but none answer my question.
I tried giving my ListView a x:Name="QuizListView" and using ElementName inside the ListView to bind the command to the button.
QuizOverview.xaml
<Page
x:Name="QuizOverviewPage"
x:Class="UwpApp.View.QuizOverview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UwpApp.View"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding QuizOverviewInstance, Source={StaticResource Locator}}">
<StackPanel Margin="20">
<TextBlock Text="Quizzen" FontSize="48"/>
<ListView x:Name="QuizListView" ItemsSource="{Binding Quizzes}" Margin="0,20" SelectionMode="Single" SelectionChanged="QuizListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<ListViewItem>
<StackPanel Orientation="Horizontal">
<Border CornerRadius="10" BorderBrush="Black" BorderThickness="2" Height="100" Width="400" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="36"></TextBlock>
</Border>
<Button VerticalAlignment="Center" Padding="20" Margin="20">Edit</Button>
<Button VerticalAlignment="Center" Padding="20" Margin="0" Command="{Binding Path=DataContext.DeleteQuizCommand, ElementName=QuizListView}" CommandParameter="{Binding}">Delete</Button>
</StackPanel>
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Padding="20" Command="{Binding CreateQuizCommand}">Add New Quiz</Button>
</StackPanel>
</Page>
QuizOverviewViewModel.cs
public class QuizOverviewViewModel : ViewModelBase
{
public string Title { get; set; }
public ObservableCollection<Quiz> Quizzes { get; set; }
public RelayCommand<Quiz> DeleteQuizCommand { get; set; }
public RelayCommand CreateQuizCommand { get; set; }
public QuizOverviewViewModel()
{
Title = "Quizzen";
Quizzes = new ObservableCollection<Quiz> { new Quiz(1, "Test Quiz 1"), new Quiz(2, "Test Quiz 2"), new Quiz(3, "Test Quiz 3") };
DeleteQuizCommand = new RelayCommand<Quiz>(DeleteQuiz);
CreateQuizCommand = new RelayCommand(CreateQuizTest);
}
public void DeleteQuiz(Quiz quiz)
{
Quizzes.Remove(Quizzes.Single(i => i.Id == quiz.Id));
}
public void CreateQuizTest()
{
Quizzes.Add(new Quiz(4, "Test Quiz Creation!"));
}
}
Quiz.cs
namespace UwpApp.Model
{
public class Quiz
{
public long Id { get; set; }
public string Name { get; set; }
public Quiz(long id, string name)
{
Id = id;
Name = name;
}
}
}
The Button does nothing with my current code, while the command does work outside of the ListView.
Databinding to command inside of listview not working in UWP/MVVM-Light
The problem is that you insert ListViewItem in to DataTemplate. It will cause that ListViewItem contains sub-ListViewItem in the Visual Tree and you could not access correct DataContext with element name in your button. Please remove ListViewItem from your code.
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border CornerRadius="10" BorderBrush="Black" BorderThickness="2" Height="100" Width="400" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="36"></TextBlock>
</Border>
<Button VerticalAlignment="Center" Padding="20" Margin="20">Edit</Button>
<Button VerticalAlignment="Center" Padding="20" Margin="0" Command="{Binding Path=DataContext.DeleteQuizCommand, ElementName=QuizListView}" CommandParameter="{Binding}">Delete</Button>
</StackPanel>
</DataTemplate>
For my current projet i want to make an UI where information are displayed on "tile" which i designed as a Grid with other Label and progress bar into. But i can't figure out how to make those tiles displayed on my main grid.
I already tried Gridview that didn't work with the exemples on the web and making someway without grid isn't really easy.
This is the grid to insert as many times as needed
<Grid Margin="10" Background="AntiqueWhite" Grid.Row="0" Grid.Column="0">
<Label Content="{Binding fieldname}" FontFamily="Arial Black" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,20,0,0"/>
<Label Content="{Binding datebegin}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="85,85,0,0" FontFamily="Arial Black" FontSize="18" />
<Label Content="{Binding dateend}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="285,85,0,0" FontFamily="Arial Black" FontSize="18" />
<ProgressBar Margin="0,150,0,0" Value="{Binding progression}" Background="White" BorderBrush="Black" VerticalAlignment="Top" Height="50" />
</Grid>
I need a way to add the exact tile produced into a grid (or anything else that can get onto a scrollviewer with 3 column and unlimited lines obviously).
Below is the xmal. I have used ItemsControl and the WrapPanel as ItemsPanelTemplate to wrap the ItemsControl's items
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Content="Add" Height="26" Width="75" Command="{Binding Add}" Grid.Row="0"/>
<ScrollViewer Height="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="1">
<ItemsControl ItemsSource="{Binding Items}" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="10,10,0,0" BorderBrush="Black" BorderThickness="1">
<StackPanel Width="200" Height="Auto">
<Label Content="{Binding fieldname}" FontFamily="Arial Black" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,10,0,0"/>
<Label Content="{Binding datebegin}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,10,0,0" FontFamily="Arial Black" FontSize="18" />
<Label Content="{Binding dateend}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,10,0,0" FontFamily="Arial Black" FontSize="18" />
<ProgressBar Value="{Binding progression}" Margin="5,10,5,10" Background="White" BorderBrush="Black" VerticalAlignment="Top" Height="20" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
And the viewmodel is
public class Item
{
public string fieldname
{
get;
set;
}
public string datebegin
{
get;
set;
}
public string dateend
{
get;
set;
}
public string progression
{
get;
set;
}
public Item(int number)
{
fieldname = "Filed name " + number;
datebegin = "12-12-12";
dateend = "14-12-12";
progression = (5 * number).ToString();
}
}
public class ViewModel
{
public ICollection<Item> Items
{
get;
private set;
}
public ViewModel()
{
Items = new ObservableCollection<Item>();
}
public ICommand Add
{
get
{
return new RelayCommand((a) =>
{
Items.Add(new Item(Items.Count));
});
}
}
}
Relaycommand
public class RelayCommand : ICommand
{
private Action<object> execute;
private Predicate<object> canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
I am trying to build a diary app that uses hub as user interface and update the UI after saving the diary. I already implemented the INotifyPropertyChanged but it didn't work. I want the item that is added after saving to appear on the hub immediately.
public class SampleDataGroup : INotifyPropertyChanged
{
public SampleDataGroup()
{
UniqueId = string.Empty;
Title = string.Empty;
Subtitle = string.Empty;
Description = string.Empty;
ImagePath = string.Empty;
Items = new ObservableCollection<DiaryData>();
}
public string UniqueId { get; private set; }
public string Title { get; private set; }
public string Subtitle { get; private set; }
public string Description { get; private set; }
public string ImagePath { get; private set; }
private ObservableCollection<DiaryData> _items;
public ObservableCollection<DiaryData> Items { get{return _items;} private set
{
OnPropertyChanged("Items");
_items = value;
} }
public override string ToString()
{
if (this.Title != null)
{
return this.Title;
}
else
{
System.Diagnostics.Debug.WriteLine("this is null at tostring");
return null;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public sealed class SampleDataSource : INotifyPropertyChanged
{
private static SampleDataSource _sampleDataSource = new SampleDataSource();
private ObservableCollection<SampleDataGroup> _groups = new ObservableCollection<SampleDataGroup>();
public ObservableCollection<SampleDataGroup> Groups
{
get { return this._groups; }
set { }
}
public static async Task<IEnumerable<SampleDataGroup>> GetGroupsAsync()
{
await _sampleDataSource.GetSampleDataAsync();
return _sampleDataSource.Groups;
}
public static async Task<SampleDataGroup> GetGroupAsync(string uniqueId)
{
System.Diagnostics.Debug.WriteLine("GetGroupAsync is entered phase 1");
await _sampleDataSource.GetSampleDataAsync();
// Simple linear search is acceptable for small data sets
System.Diagnostics.Debug.WriteLine("GetGroupAsync is entered phase 2");
var matches = _sampleDataSource.Groups.Where((group) => group.UniqueId.Equals(uniqueId));
if (matches.Count() == 1) return matches.First();
return null;
}
public static async Task<DiaryData> GetItemAsync(string uniqueId)
{
await _sampleDataSource.GetSampleDataAsync();
System.Diagnostics.Debug.WriteLine("GetItemAsync is entered");
// Simple linear search is acceptable for small data sets
var matches = _sampleDataSource.Groups.SelectMany(group => group.Items).Where((item) => item.UniqueId.Equals(uniqueId));
if (matches.Count() == 1) return matches.First();
else return null;
}
private async Task GetSampleDataAsync()
{
System.Diagnostics.Debug.WriteLine("GetSampleDataAsync is entered");
//if (this._groups.Count != 0)return;
Uri dataUri = new Uri("ms-appdata:///local/data.json");
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(dataUri);
string jsonText = await FileIO.ReadTextAsync(file);
JsonArray jsonArray = JsonArray.Parse(jsonText);
SampleDataGroup group = new SampleDataGroup();
foreach (JsonValue itemValue in jsonArray)
{
JsonObject itemObject = itemValue.GetObject();
group.Items.Add(new DiaryData(itemObject["Title"].GetString(),
itemObject["Content"].GetString(),
itemObject["Coordinate"].GetString(),
itemObject["UniqueId"].GetString(),
itemObject["ImagePath"].GetString(),
itemObject["VideoPath"].GetString()));
System.Diagnostics.Debug.WriteLine(itemObject["Title"].GetString());
}
this.Groups.Add(group);
System.Diagnostics.Debug.WriteLine("GetSampleDataAsync is finished");
}
//}
public event PropertyChangedEventHandler PropertyChanged;
}
here's my XAML File
<Page
x:Class="DiaryAppHub.HubPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DiaryAppHub"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="using:DiaryAppHub.Data"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
d:DataContext="{Binding Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:data.json}}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="HubSectionHeaderTemplate">
<TextBlock Margin="0,0,0,-9.5" Text="{Binding}"/>
</DataTemplate>
<!-- Grid-appropriate item template as seen in section 2 -->
<DataTemplate x:Key="Standard200x180TileItemTemplate">
<Grid Margin="0,0,9.5,9.5" Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="138.5" Width="138.5"/>
<TextBlock Text="{Binding Title}" VerticalAlignment="Bottom" Margin="9.5,0,0,6.5" Style="{ThemeResource BaseTextBlockStyle}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="StandardTripleLineItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,9.5,0,0" Grid.Column="0" HorizontalAlignment="Left">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="79" Width="79"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock Text="{Binding Description}" Style="{ThemeResource ListViewItemContentTextBlockStyle}" Foreground="{ThemeResource PhoneMidBrush}" />
<TextBlock Text="{Binding Subtitle}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="StandardDoubleLineItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,9.5,0,0" Grid.Column="0" HorizontalAlignment="Left">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="79" Width="79"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}" Foreground="Black"/>
<TextBlock Text="{Binding Subtitle}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" Foreground="DimGray"/>
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
<Page.BottomAppBar>
<CommandBar Background="Transparent">
<AppBarButton Icon="Add" Label="Add" Click="add_onclick"/>
<AppBarButton Icon="Add" Label="Shake it!" />
</CommandBar>
</Page.BottomAppBar>
<Grid x:Name="LayoutRoot">
<Hub x:Name="Hub" x:Uid="Hub" Header="diary app hub" Margin="0,0,0,-59" Foreground="DimGray">
<Hub.Background>
<ImageBrush ImageSource="ms-appx:/Assets/desk_paper.png" Stretch="None"/>
</Hub.Background>
<!--<HubSection x:Uid="HubSection1" Header="SECTION 1" DataContext="{Binding Groups}" HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<ListView
ItemsSource="{Binding}"
IsItemClickEnabled="True"
ItemClick="GroupSection_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,27.5">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</HubSection>-->
<HubSection x:Uid="HubSection5" Header="Recent"
DataContext="{Binding Groups[0]}" HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<ListView
AutomationProperties.AutomationId="ItemListViewSection5"
AutomationProperties.Name="Items In Group"
SelectionMode="None"
IsItemClickEnabled="True"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource StandardDoubleLineItemTemplate}"
ItemClick="ItemView_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
</ListView>
</DataTemplate>
</HubSection>
<HubSection x:Uid="HubSection2" Header="All notes" Width ="Auto"
DataContext="{Binding Groups[0]}" HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}" Height="659" >
<DataTemplate>
<GridView
Margin="0,9.5,0,0"
ItemsSource="{Binding Items}"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Items In Group"
ItemTemplate="{StaticResource Standard200x180TileItemTemplate}"
SelectionMode="None"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</DataTemplate>
</HubSection>
</Hub>
</Grid>
You need to raise the PropertyChanged event for the model's properties. The UI doesn't get notified as properties like Title,Subtitle don't raise the PropertyChanged event when they are modified. It should be like this:
private string _title;
public string Title
{
get
{
return _title;
}
set
{
if(_title!=value)
{
_title=value;
OnPropertyChanged("Title");
}
}
}
Do this similarly for other properties. Also, you don't need to raise the PropertyChanged event for an ObservableCollection as an ObservableCollection implements INotifyPropertyChanged by default.