I am trying to do ListView with ItemTemplate with CheckBox and TextBlock control and bind data to it. However, the list does not display added items.
Here is my XAML code:
<ListView x:Name="WarrantyCategory_ListView" HorizontalAlignment="Stretch" Grid.Row="3" VerticalAlignment="Center" SelectionMode="Single" Header="Category:" FontSize="16" Foreground="#FFC7C7C7" Margin="10,0" ItemsSource="{Binding WarrantyCategories}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<CheckBox x:Name="Description_CheckBox" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
<TextBlock x:Name="DescriptionItem_TextBlock" Text="{Binding Category, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is my WarrantyCategory class:
public class WarrantyCategory
{
public bool IsChecked { get; set; }
public string Category { get; set; }
public WarrantyCategory(bool isChecked, string category)
{
IsChecked = isChecked;
Category = category;
}
}
and my Page code behind:
public AddWarrantyDescriptionPage()
{
this.InitializeComponent();
DisplayInformation.AutoRotationPreferences = DisplayOrientations.Portrait;
InitializeCategoryList();
}
private void InitializeCategoryList()
{
WarrantyCategories.Add(new WarrantyCategory(false, "Computers"));
WarrantyCategories.Add(new WarrantyCategory(false, "Mobile"));
WarrantyCategories.Add(new WarrantyCategory(false, "Music"));
WarrantyCategories.Add(new WarrantyCategory(false, "Video"));
}
Related
I have ItemsControl and a DataGrid in a WPF UserControl. this is how it looks like
when the "Add to card" button is pressed a ViewModel instance is added to ObservableCollection bound to the DataGrid.
<ItemsControl
ItemsSource="{Binding Meals}"
x:Name="MealList"
Margin="5">
<ItemsControl.ItemTemplate>
<DataTemplate>
<components:MealCardCustomer
BorderBrush="OrangeRed"
BorderThickness="5px"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<ScrollViewer
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled">
<DataGrid
HorizontalAlignment="Stretch"
IsReadOnly="True"
Background="Orange"
x:Name="OrderedMeals"
SelectionMode="Single"
ItemsSource="{Binding OrderedMeals, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
SelectedIndex="{Binding SelectedOrderedMeal, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"
FontSize="26"
Grid.Column="0"
Grid.Row="0"
Margin="5"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, Mode=OneWay}" Header="Name" />
<DataGridTextColumn Binding= "{Binding Price, Mode=OneWay}" Header="Price" />
<DataGridTextColumn Binding="{Binding Ingredients, Mode=OneWay}" Header="Ingredients" />
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
The problem is that sometimes when I add new items it appears like an empty column.
I tried to add a button which refreshes the data grid but when pressed it makes the all of the items blank.
Also I've wrapped the DataGrid in a ScrollViewer with a horizontal scroll which for some reason doesn't work.
That's the ViewModel of the View
private string? address;
public string? Address
{
get { return address; }
set { address = value; OnPropertyChaneg(nameof(Address)); }
}
private int selectedOrderedMeal = -1;
public int SelectedOrderedMeal
{
get { return selectedOrderedMeal; }
set { selectedOrderedMeal = value; OnPropertyChaneg(nameof(SelectedOrderedMeal)); }
}
private ObservableCollection<MealCardCustomerViewModel> meals;
public ObservableCollection<MealCardCustomerViewModel> Meals
{
get { return meals; }
set { meals = value; }
}
private ObservableCollection<MealCardCustomerViewModel> orderedMeals;
public ObservableCollection<MealCardCustomerViewModel> OrderedMeals
{
get { return orderedMeals; }
set { orderedMeals = value; OnPropertyChaneg(nameof(OrderedMeals)); }
}
public BaseCommand RemoveCommand { get; }
public BaseCommand FinishOrderCommand { get; }
public NavigateCommand NavigateToCustomerListOfOtders { get; }
public BaseCommand LoadMealsCommand { get; }
public CustomerOrderingViewModel(NavigationService customerListOfOrdersNavigationService, NavigationService helpNavigationService, IMealService mealService)
: base(helpNavigationService, mealService)
{
Meals = new ObservableCollection<MealCardCustomerViewModel>();
OrderedMeals = new ObservableCollection<MealCardCustomerViewModel>();
RemoveCommand = new RemoveMeal(this);
FinishOrderCommand = new FinishOrder(this, customerListOfOrdersNavigationService);
NavigateToCustomerListOfOtders = new NavigateCommand(customerListOfOrdersNavigationService);
LoadMealsCommand = new LoadMeals<CustomerOrderingViewModel>(this);
}
public static CustomerOrderingViewModel LoadViewModel(NavigationService customerListOfOrders, NavigationService helpNavigationService, IMealService mealService)
{
CustomerOrderingViewModel viewModel = new CustomerOrderingViewModel(customerListOfOrders, helpNavigationService, mealService);
viewModel.LoadMealsCommand.Execute(null);
return viewModel;
}
public override void LoadMealsList(List<Meal> meals)
{
Meals.Clear();
foreach (var meal in meals)
{
Meals.Add(new MealCardCustomerViewModel(meal,this));
}
}
That the Views which act like ItemTemplates for the ItemsControl
<Image
Source="{Binding MealImage, Converter ={StaticResource imageConverter}, Mode=TwoWay, TargetNullValue=DefaultImage}"
Stretch="Uniform"/>
<DockPanel
Grid.Row="1"
VerticalAlignment="Center"
Margin="5">
<TextBlock
FontSize="20"
Margin="5"
Text="Name :"/>
<TextBox
Text="{Binding Name,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontSize="20"
Margin="5"/>
</DockPanel>
<DockPanel
Grid.Row="2"
VerticalAlignment="Center"
Margin="5">
<TextBlock
FontSize="20"
Margin="5"
Text="Price :"/>
<TextBox
Text="{Binding Price, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat={}{0:f2}}"
FontSize="20"
Margin="5"/>
</DockPanel>
<DockPanel
Grid.Row="3"
VerticalAlignment="Center"
Margin="5">
<TextBlock
FontSize="20"
Margin="5"
Text="Ingredients:"/>
<TextBox
Text="{Binding Ingredients, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontSize="20"
Margin="5"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Visible"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
/>
</DockPanel>
<Button
Command="{Binding AddToCardCommand}"
Background="OrangeRed"
Grid.Row="4"
Margin="10 5 10 5"
Content="Add to cart"
FontSize="20"/>
and that's the command that adds the item to the ObservableCollection
private CustomerOrderingViewModel customerOrderingViewModel;
private MealCardCustomerViewModel mealCardCustomerViewModel;
public AddToCard(CustomerOrderingViewModel customerOrderingViewModel, MealCardCustomerViewModel mealCardCustomerViewModel)
{
this.customerOrderingViewModel = customerOrderingViewModel;
this.mealCardCustomerViewModel = mealCardCustomerViewModel;
}
public override void Execute(object? parameter)
{
customerOrderingViewModel.OrderedMeals.Add(mealCardCustomerViewModel);
}
The problem was with the images in the objects which are non existing right now and so they are null.
For some reason the null value cause infinite loop in the converter and so the view models could not load the properties of the entity but the collection could read that the count was changed thus displaying the empty rows.
The way you add items to the cart is not thread safe.
Immagine the AddToCart() being called wich will update your customerOrderingViewModel and mealCardCustomerViewModel. Then immagine that before Execute is called, some other thread changes customerOrderingViewModel or mealCardCustomerViewModel. This could result in Execute() adding the wrong (or a Null) meal to your order.
If that is the reason for your error, the following code shoud solve it:
public AddToCard(CustomerOrderingViewModel customerOrderingViewModel, MealCardCustomerViewModel mealCardCustomerViewModel)
{
customerOrderingViewModel.OrderedMeals.Add(mealCardCustomerViewModel);
this.customerOrderingViewModel = customerOrderingViewModel;
this.mealCardCustomerViewModel = mealCardCustomerViewModel;
}
If you dont need customerOrderingViewModel and mealCardCustomerViewModel in the class owning AddToCart(), you could even spare those variables completely.
Side note:
If you dont plan on changing the observable collections but only their content, you can simply declare them as public fiels and not as propertys. The setter of the propertys wil only be accessed when thwo whole ObservableCollection object is changed but not if its content is changed. PropertyChanged notifications for changes inside the ObservableCollection are handlled by the ObservableCollection implementation.
I have a Listbox which is bound to a DataTemplate that has another Listbox on it.
On DataTemplate there is a button that I want to use for adding items to DataTemplate ListBox, but I can't find a solution to do this.
Here is my listbox:
<Button Width="200" Content="Add Question" x:Name="btnAddQuestion" Click="btnAddQuestion_Click"/>
<StackPanel Orientation="Horizontal">
<ListBox Margin="5" x:Name="lvQuestions" ItemTemplate="{StaticResource TemplateQuestionTitle}">
</ListBox>
</StackPanel>
And this is DataTemplate:
<DataTemplate x:Key="TemplateQuestionTitle">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox materialDesign:HintAssist.Hint="Enter question" MinWidth="200" Style="{StaticResource MaterialDesignFloatingHintTextBox}"/>
<Button Content="+" Command="{Binding Source={x:Reference ThisPage},Path=DataContext.Command}" />
</StackPanel>
<ListBox ItemsSource="{Binding MyItems}" MinHeight="50">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox>
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
This is code behind on my page:
public partial class UIBuilder:Window
{
private CommandVm _commandVm;
public UIBuilder()
{
InitializeComponent();
_commandVm = new CommandVm();
DataContext = _commandVm;
}
private void btnAddQuestion_Click(object sender, RoutedEventArgs e)
{
lvQuestions.Items.Add(null);
}
}
I have implemented this code on my ViewModel in order to add items to datatemplate ListBox:
public class CommandVm
{
public ObservableCollection<TextBox> MyItems { get; set; }
public CommandVm()
{
MyItems = new ObservableCollection<TextBox>();
Command = new RelayCommand<TextBox>(Execute);
}
private void Execute(TextBox textBox)
{
MyItems .Add(textBox);
}
public ICommand Command { get; set; }
}
I use to catch the Execute() function on button "+" click command, but my code doesn't add any ListBox item.
MyItems is a property of the parent view model which means that you should bind to it like this:
<ListBox ItemsSource="{Binding DataContext.MyItems,
RelativeSource={RelativeSource AncestorType=Window}}" MinHeight="50">
This also means that you are using one single collection of items for all questions. Besides this obvious design flaw, a view model should not contain any TextBox elements. This basically breaks what the MVVM pattern is all about.
What you should do to make this example MVVM compliant is to create a Question class that has a collection of items, e.g.:
public class Question
{
public Question()
{
AddAnswerCommand = new RelayCommand<object>(Execute);
}
private void Execute(object obj)
{
Items.Add(new Answer());
}
public ObservableCollection<Answer> Items { get; }
= new ObservableCollection<Answer>();
public ICommand AddAnswerCommand { get; }
}
public class Answer { }
The window's view model should then have a collection of questions:
public class CommandVm
{
public CommandVm()
{
AddQuestionCommand = new RelayCommand<object>(Execute);
}
public ObservableCollection<Question> Questions { get; }
= new ObservableCollection<Question>();
public ICommand AddQuestionCommand { get; }
private void Execute(object obj)
{
Questions.Add(new Question());
}
}
The view and the bindings could then be defined like this:
<Window.Resources>
<DataTemplate x:Key="TemplateQuestionTitle">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox MinWidth="200" />
<Button Content="+" Command="{Binding AddAnswerCommand}" />
</StackPanel>
<ListBox ItemsSource="{Binding Items}" MinHeight="50">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Width="200" Content="Add Question" Command="{Binding AddQuestionCommand}"/>
<ListBox Margin="5"
ItemsSource="{Binding Questions}"
ItemTemplate="{StaticResource TemplateQuestionTitle}" />
</StackPanel>
This setup lets you add individual elements to each separate question.
I have created a treeview in my xaml.
<TreeView Name="exportTreeView" ItemsSource="{Binding}" Width="350" >
<TreeView.Resources>
<DataTemplate x:Key="layersTemplate">
<StackPanel Orientation="Horizontal" Margin="10,0,0,0">
<CheckBox Foreground="White" IsChecked="{Binding IsToBeExported}" VerticalAlignment="Center" />
<Label Style="{StaticResource baseStyle}" Content="{Binding Path=Name}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="objectTemplate" ItemsSource="{Binding Path=LayersList}" ItemTemplate="{StaticResource ResourceKey=layersTemplate}">
<StackPanel Orientation="Horizontal" Height="15" Margin="10,0,0,0">
<CheckBox Foreground="White" IsChecked="{Binding IsToBeExported}" VerticalAlignment="Center" />
<Label Style="{StaticResource baseStyle}" Content="{Binding Path=Name}" VerticalAlignment="Center" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=ObjectList}" ItemTemplate="{StaticResource ResourceKey=objectTemplate}">
<StackPanel Orientation="Horizontal" Margin="10,0,0,0">
<CheckBox Foreground="White" IsChecked="{Binding IsToBeExported}" VerticalAlignment="Center" />
<Label Style="{StaticResource baseStyle}" Content="{Binding Path=Name}" VerticalAlignment="Center" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
The tree structure is like below. Each Parent can have any number of children & each Child can have any number of Grandchildren. Multiple selection is allowed too.
Parent
-Child
--Grandchild
I have checkboxes for all levels. I am not getting how to access its nodes individually and also how to use the tree data.
In my VM class, I set the datacontext of this TreeView to a 3 class list like below:
public class MProject
{
public string Name { get; set; }
public bool IsToBeExported { get; set; }
public List<MWorkObject> ObjectList { get; set; }
}
public class MWorkObject
{
public string Name { get; set; }
public bool IsToBeExported { get; set; }
public List<MLayer> LayersList { get; set; }
}
public class MLayer
{
public string Name { get; set; }
public bool IsToBeExported { get; set; }
}
My requirement is:
Selecting the parent should select all its child and grandchild.
How to identify in the code which item is selected ? Need it to do further processing.
Please help.
You need to implement INotifyPropertyChanged for your classes. Then
in setter of IsToBeExported in MProject handle all children (set
IsToBeExported to what you need). The binding makes the change
visible in tree
if IsToBeExported set to true, then it is selected
Example:
public class ViewBase :INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
public class MProject : ViewBase
{
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
private string _name;
...
}
I'm trying to data bind a collection ("Dashboard") that contains a ObservableCollection property.
I have managed to databind the dashboard class without any issues. I cant however work out how to databind to the Release collection that is contained within the dashboard class.
The issue seems to be on the GridView which is databound to the Releases property of the Dashboard class. The Stack Panel around the GridView is working correctly.
The classes
public class Dashboard
{
public Dashboard(String id, String projectName)
{
this.Id = id;
this.ProjectName = projectName;
this.Releases = new ObservableCollection<Release>();
}
public string Id { get; private set; }
public string ProjectName { get; private set; }
public ObservableCollection<Release> Releases { get; private set; }
public override string ToString()
{
return this.ProjectName;
}
}
public class Release
{
public Release(string environmentName, string releaseVersion, string state, string releaseDate)
{
EnvironmentName = environmentName;
ReleaseVersion = releaseVersion;
State = state;
ReleaseDate = releaseDate;
}
public string EnvironmentName { get; private set; }
public string ReleaseVersion { get; private set; }
public string State { get; private set; }
public string ReleaseDate { get; private set; }
}
The XAML
<HubSection x:Uid="Dashboard" x:Name="Dashboard" Header="Dashboard" DataContext="{Binding Dashboard}">
<DataTemplate>
<GridView x:Uid="DashboardGrid" x:Name="DashboardGrid" ItemsSource="{Binding}" ItemTemplate="{StaticResource Standard200x180TileItemTemplate}" >
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</DataTemplate>
</HubSection>
The data template
<DataTemplate x:Key="Standard200x180TileItemTemplate">
<StackPanel DataContext="{Binding}" >
<TextBlock Text="{Binding ProjectName}" Grid.Column="0" Style="{ThemeResource BaseTextBlockStyle}" Typography.Capitals="SmallCaps" Grid.Row="0" IsTextScaleFactorEnabled="False"/>
<GridView Grid.Row="1" DataContext="{Binding Releases}">
<TextBlock Text="{Binding EnvironmentName}" />
<TextBlock Text="{Binding ReleaseVersion}" />
<TextBlock>hello</TextBlock>
<Border Background="#FF0CB90C" Height="110" Width="110" HorizontalAlignment="Left" Margin="0,0,10,0">
</Border>
</GridView>
</StackPanel>
</DataTemplate>
Set ItemsSource of GridView not DataContext and then use another DataTemplate
<StackPanel>
<TextBlock Text="{Binding ProjectName}" Grid.Column="0" Style="{ThemeResource BaseTextBlockStyle}" Typography.Capitals="SmallCaps" Grid.Row="0" IsTextScaleFactorEnabled="False"/>
<GridView Grid.Row="1" ItemsSource="{Binding Releases}">
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding EnvironmentName}" />
<TextBlock Text="{Binding ReleaseVersion}" />
<TextBlock>hello</TextBlock>
<Border Background="#FF0CB90C" Height="110" Width="110" HorizontalAlignment="Left" Margin="0,0,10,0">
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</StackPanel>
i want to display a string and three bools in a listbox, therefor i made an ItemTemplate:
<ListBox x:Name="lstVars" Margin="10,41" Grid.ColumnSpan="4" Grid.Column="1">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel Height="30">
<TextBlock Text="{Binding description}" VerticalAlignment="Center" HorizontalAlignment="Left" Width="200"/>
<CheckBox Margin="3" Content="a" IsChecked="{Binding save}" Width="200"/>
<CheckBox Margin="3" Content="b" IsChecked="{Binding displayBoard}" Width="200"/>
<CheckBox Margin="3" Content="c" IsChecked="{Binding displayGraph}" Width="200"/>
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
i made a testclass because with my original class it doesnt work (but the same "error" occurs with the testclass):
public class test
{
public String description;
public bool save { get; set; }
public bool displayBoard { get; set; }
public bool displayGraph { get; set; }
public test(String description, bool save, bool displayBoard, bool displayGraph)
{
this.description = description;
this.save = save;
this.displayBoard = displayBoard;
this.displayGraph = displayGraph;
}
}
when i add some values to the listbox, the string is not displayed
lstVars.Items.Add(new test("teststring", true, false, true));
i first thought the text would just be in a not visible row but when i write Text="123test" instead of Text="{Binding description}" it displays 123test like it should.
You must declare binding objects as properties. Like your bool properties.
public String description {get; set;}