I want to have a list of checkboxes that binds to a collection. So when options are selected, they are added to the list --- when options are deselected, they get removed.
Have tried a number of approaches, but failed to solve this.
Model
public enum WeatherType
{
Rainy,
Sunny,
Cloudy,
Windy
}
ViewModel
public class WeatherViewModel : INotifyPropertyChanged
{
public ObservableCollection<WeatherType> WeatherTypes {get;set;}
...
}
XAML
<ObjectDataProvider x:Key="weather"
MethodName="GetValues"
ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="business:WeatherType" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
...
<ItemsControl Grid.Row="4"
Grid.Column="1"
ItemsSource="{Binding Source={StaticResource weather}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Declare a view model for weather type:
public class WeatherTypeViewModel
{
public WeatherType WeatherType { get; set; }
public bool IsChecked { get; set; }
}
Change your view model like this:
public class WeatherViewModel : INotifyPropertyChanged
{
public ObservableCollection<WeatherTypeViewModel> WeatherTypes {get;set;}
...
}
and view - like this:
<ItemsControl Grid.Row="4"
Grid.Column="1"
ItemsSource="{Binding WeatherTypes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding WeatherType}" IsChecked="{Binding IsChecked}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Related
I want to repeat the Border element inside the outer WrapPanel:
<WrapPanel x:Name="filterWrapper" Orientation="Horizontal">
<Border>
<WrapPanel>
<TextBlock Text="{Binding FilterName}"/>
<TextBlock Text="x"/>
</WrapPanel>
</Border>
</WrapPanel>
This is the depending class:
public class FilterField
{
public String FilterName { get; set; }
}
I have a collection of FilterField objects, which should result in:
<WrapPanel x:Name="filterWrapper" Orientation="Horizontal">
<Border>
<WrapPanel>
<TextBlock Text="Test_1"/>
<TextBlock Text="x"/>
</WrapPanel>
</Border>
<Border>
<WrapPanel>
<TextBlock Text="Test_2"/>
<TextBlock Text="x"/>
</WrapPanel>
</Border>
<Border>
<WrapPanel>
<TextBlock Text="Test_3"/>
<TextBlock Text="x"/>
</WrapPanel>
</Border>
</WrapPanel>
How can I achieve this?
You can use the ItemsControl to display a collection of items with a specific template in a specific Panel:
<ItemsControl ItemsSource="{Binding Items}">
<!-- Place all items in a WrapPanel -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="filterWrapper" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Define the look of each item -->
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type wpfapp1:FilterField}">
<Border>
<WrapPanel>
<TextBlock Text="{Binding FilterName}"/>
<TextBlock Text="x"/>
</WrapPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The DataTemplate you define in ItemsControl.ItemTemplate gets applied to all items in the source collection, and each item gets added in the ItemsControl.ItemsPanel that you define, here a WrapPanel.
This is assuming you have a a collection Items of type List<FilterField> in your view model, like this:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
DataContext = new MyViewModel();
}
}
public class MyViewModel {
public List<FilterField> Items { get; set; } = new List<FilterField> {
new FilterField() { FilterName = "Test_1"},
new FilterField() { FilterName = "Test_2"},
new FilterField() { FilterName = "Test_3"},
};
}
public class FilterField {
public string FilterName { get; set; }
}
Lets say i have Model
public class Person
{
public string Name { get; set; }
}
And in my ViewModle i have this list:
ObservableCollection<Person> People;
Normally the binding syntax looks like this:
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
What i want to achive is to have for each row 3 people.
So let's say People = {A,B,C,D,E,F,G}
I want to display it like:
A B C
D E F
G
What is the proper way to achieve this ?
You could Use a UniformGrid as ItemsPanel like this:
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I'm trying to display a list of records by unit in separate columns. Everything is working except the binding to filter by unit.
I may be going about this completely the wrong way, but here's what I've got.
public class Record : INotifyPropertyChanged
{
public string Name { get; set; }
public string Unit { get; set; }
}
public class UnitList
{
public ObservableCollection<Record> Items { get; set; }
}
I'm using this markup extension code to do the filtering. In WPF can you filter a CollectionViewSource without code behind?
I can set the PropertyFilter Value statically and it filters fine, but all columns give me the same values. tbUnitName displays the correct value for Unit, but I can't get that same value passed in to the PropertyFilter Value I've tried the ProxyElement/FrameworkElement binding trick and as many different combinations of binding to parent elements as I can think of but I still get 'null' in the filter code.
In the window codebehind I've got:
List<string> names = (from s in this.SelectedSheets.Items
where !string.IsNullOrWhiteSpace(s.Unit)
select s.Unit).Distinct().ToList();
UnitNames = new ObservableCollection<string>(names);
lbUnits.ItemsSource = UnitNames;
Here's the XAML:
<ListBox Name="lbUnits" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Name="spRoster" Orientation="Horizontal" CanHorizontallyScroll="True" CanVerticallyScroll="False" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox x:Name="tbUnitName" Text="{Binding Path=.}" />
<ListBox ItemsSource="{Binding }" ItemTemplate="{StaticResource listItemTemplate}" Margin="0,20,0,0" Name="lb1" HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="150" ScrollViewer.CanContentScroll="False" >
<ListBox.DataContext>
<CollectionViewSource Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=Items}" >
<!--Filter="FilterOutB"-->
<CollectionViewSource.Filter>
<local:Filter>
<local:PropertyFilter PropertyName="Unit" Value="{Binding Path=.}" />
</local:Filter>
</CollectionViewSource.Filter>
</CollectionViewSource>
</ListBox.DataContext>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel MaxWidth="{Binding ActualWidth, ElementName=lb1}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I think I've solved it. I added
<Grid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</Grid.Resources>
and changed the filter to
<local:PropertyFilter PropertyName="Unit" Value="{Binding Source={StaticResource proxy}}" />
A complete new bee in XAML and WPF shifted from MVC 1 week back
Requirement:
Display Customer Name and all corresponding Brands
1 Customer can have Many Brands
Issue
Unable to display the corresponding brands
Reference
Similar SO Thread
Code
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new MyDeviceList();
InitializeComponent();
}
}
public class MyDeviceList
{
Entities ent = new Entities();
public ObservableCollection<myCustomers> Customers { get; set; }
public void GetCustomersBrand()
{
var custall = (from c in ent.Customers
select new myCustomers{ name = c.Name, brands = c.Brands.ToList() }).ToList();
Customers = new ObservableCollection<myCustomers>(custall);
}
public MyDeviceList()
{
GetCustomersBrand();
}
}
//just a dummy class
public class Brand
{
public string Name { get; set; }
public int Customerid { get; set; }
}
public class myCustomers
{
public string name { get; set; }
public List<Brand> brands { get; set; }
}
XAML
<Window x:Class="DeviceListCreate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:DeviceListCreate"
Title="MainWindow" WindowState="Maximized">
<Window.Resources>
<DataTemplate x:Key="GroupTemplate" DataType="{x:Type my:myCustomers}">
<ItemsControl ItemsSource="{Binding brands}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Customers}" Name="tStack" Grid.Column="0" Margin="33,10,42,10">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding name}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Kindly guide which direction I should be heading now
Thanks in advance
When constructing a UI like this, you just have to build it up part by part. Concentrating on each part individually makes the whole task more manageable. Try this:
<Grid>
<ItemsControl ItemsSource="{Binding Customers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border CornerRadius="5" BorderBrush="RoyalBlue" BorderThickness="1"
Padding="5" Margin="5">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" Margin="5" />
<ItemsControl ItemsSource="{Binding brands}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding name}" Margin="5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Say I have the following class, Employee
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public ObservableCollection<Employee> Underlings { get; set; }
}
And then I have the following XAML, bound to an ObservableCollection<Employee> MyEmployees
<ListBox ItemsSource="{Binding Path=MyEmployees}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Employee.Id}">
<TextBlock Text="{Binding Path=Employee.Name}"></TextBlock>
<!-- Here's where I declare my underlings -->
<ListBox ItemsSource="{Binding Path=Employee.Underlings}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Employee.Id}">
<TextBlock Text="{Binding Path=Employee.Name}"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This allows each employee in the collection MyEmployees to have some underlings. But those underlings are also of type employee, and could have their own underlings. How do I cater for those additional levels without making my XAML very complex?
Is there some way to declare my DataTemplate separately and allow it to be referenced within itself?
Do I have to do all this from code-behind instead?
(I realise the XAML above may not be 100% correct, its just an example)
therefore you have to use a TreeView and not a ListBox. And You have to specify a HierarchicalDataTemplate.
You could define the DataTemplate inside the ListBoxes Resources as the default template for Employees:
<ListBox ItemsSource="{Binding MyEmployees}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type myns:Employee}">
<Grid Tag="{Binding Id}">
<TextBlock Text="{Binding Name}"></TextBlock>
<ListBox ItemsSource="{Binding Underlings}" />
</Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>