I have a TabControl which has several TabItems. In each TabItem, there are a number of Buttons. A tabItem is stand for a room, and in the room there are several tables (Button)
I have bound one TabItem, but I am not sure how to bind lists of TabItems in a TabControl.
MainWindow:
<TabControl Grid.Row="0" Name="tabTables" Margin="-1 -5 -1 -1" Background="AliceBlue" BorderBrush="White">
<TabItem Visibility="Collapsed">
<ItemsControl ItemsSource="{Binding TableCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="canvas1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:TableViewModel}">
<Button Uid="{Binding TableName}" ContentStringFormat="{Binding TableGuestCount}" Style="{StaticResource ResourceKey=BtnTableEmpty}" Width="84" Height="90" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=TablePosX}" />
<Setter Property="Canvas.Top" Value="{Binding Path=TablePosY}" />
<Setter Property="Tag" Value="{Binding Path=TableID}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</TabItem>
</TabControl>
TableAreaViewModel:
public class TableAreaViewModel:BaseViewModel
{
public TableAreaViewModel()
{
TableCollection = new ObservableCollection<TableViewModel>();
}
public AreaViewModel Area { get; set; }
public ObservableCollection<TableViewModel> TableCollection { get; set;
}
}
AreaViewModel:
public class AreaViewModel: BaseViewModel
{
TableGrp _tbGrp = null;
public AreaViewModel(TableGrp tbGrp)
{
_tbGrp = tbGrp;
}
public string GrpID { get { return _tbGrp.GrpID; } }
public string GrpName { get { return _tbGrp.GrpName; } }
}
}
TableViewModel:
public class TableViewModel : BaseViewModel
{
private Table _table = null;
public TableViewModel(Table tb)
{
_table = tb;
}
public string TableID
{
get { return _table.TableID; }
set { _table.TableID = value; }
}
public string TableName
{
get { return _table.Name; }
set { _table.Name = value; }
}
public string TableGuestCount
{
get
{
int customerCnt = _table.CustCount ?? 0;
return string.Format("{0}/{1}", customerCnt, _table.MaxCount);
}
}
public double TablePosX
{
get { return _table.ShowXValue ?? 10; }
set { _table.ShowXValue = value; }
}
public double TablePosY
{
get { return _table.ShowYValue ?? 10; }
set { _table.ShowYValue = value; }
}
}
TableAreaListViewModel:
public class TableAreaListViewModel
{
public TableAreaListViewModel()
{
TableAreaCollection = new ObservableCollection<TableAreaViewModel>();
LoadTables();
}
public ObservableCollection<TableAreaViewModel> TableAreaCollection { get; set; }
private void LoadTables()
{
TableManager mgr = new TableManager();
var list = mgr.LoadTableAreas();
foreach (var tb in list)
{
TableAreaViewModel ta = new TableAreaViewModel();
ta.Area = new AreaViewModel(tb.TableGroup);
foreach(var item in tb.Tables )
{
ta.TableCollection.Add(new TableViewModel(item));
}
TableAreaCollection.Add(ta);
}
}
}
You would want to bind the ItemSource for the TabControl to you collection to generate the tabs. Something like this:
<TabControl ItemsSource="{Binding TableAreaCollection}">
<TabControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding TableCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="canvas1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:TableViewModel}">
<Button Uid="{Binding TableName}" ContentStringFormat="{Binding TableGuestCount}" Style="{StaticResource ResourceKey=BtnTableEmpty}" Width="84" Height="90" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=TablePosX}" />
<Setter Property="Canvas.Top" Value="{Binding Path=TablePosY}" />
<Setter Property="Tag" Value="{Binding Path=TableID}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
Related
I have this object of class type HouseInfo that contains a list property:
public class HouseInfo
{
public string House
{
get;
set;
}
public List<String> Details
{
get;
set;
}
}
public List<HouseInfo> HouseInfos { get; set; }
I am successfully binding the House property to main items of combo box using ItemSource property in xaml but can't figure out the binding of Details to their respective submenus.
<ComboBox x:Name="Houses1"
Grid.Row="1"
Grid.Column="4"
ItemsSource="{Binding HouseInfos}"
Padding="0"
DisplayMemberPath="House"
VerticalContentAlignment="Center"
VerticalAlignment="Top"
HorizontalContentAlignment="Stretch"
Margin="0,0,0,2">
</ComboBox>
I tried customizing menuitems in xaml but I get the error "itemsCollection must be empty before using items Source."
How do I get the Details list in each menu item as submenu items?
Any help would be appreciated. Thanks in advance.
Update:
I have bound submenu items as well but they are not visible. I am sure they have bound successfully as it generates submenu items equal to the count of the list inside the details property list of the object. This is the updated xaml for the menu:
<Menu x:Name="menu"
VerticalAlignment="Top"
Grid.Row="1"
Grid.Column="4"
Height="19">
<MenuItem ItemsSource="{Binding HouseInfos}"
Padding="0"
Background="#0068FF11"
VerticalAlignment="Top"
RenderTransformOrigin="0.5,0.5"
Height="19"
Width="105">
<MenuItem.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform X="0.5" />
</TransformGroup>
</MenuItem.RenderTransform>
<MenuItem.Header>
<Label x:Name="headerYears"
Margin="0"
Padding="0"
Content="Houses"
Background="#00FF0000"
MaxHeight="18"
UseLayoutRounding="False"
RenderTransformOrigin="0,0"
HorizontalContentAlignment="Center" />
</MenuItem.Header>
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header"
Value="{Binding House}" />
<Setter Property="ItemsSource"
Value="{Binding InfoPoints}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
Here is the image of menu which is populated but not visible.
Bound but invisible submenu items
Try using the DataSource property of the combobox. You can assign HouseInfos.House1.
What I did was I dynamically assign them to the combobox
comboBox1.DataSource = HouseInfo.House1.Details;
comboBox1.DisplayMember = "HouseDetails";
comboBox1.ValueMember = "HouseDetailsID";
Or you can try something like the above.
Use this structure. I matched the names with your own names.
MainWindw.xaml
<Window x:Class="MyNameSpace.MainWindow"
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:MyNameSpace"
mc:Ignorable="d"
Title="TestMenu" Height="450" Width="800">
<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding MenuItems}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:HouseInfo}" ItemsSource="{Binding Path=Details}">
<TextBlock Text="{Binding House}"/>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
<Grid>
</Grid>
</DockPanel>
</Window>
MainWindow.cs
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace MyNameSpace
{
/// <summary>
/// Interaction logic for MainWindw.xaml
/// </summary>
public partial class MainWindw : Window
{
public List<HouseInfo> MenuItems { get; set; }
public MainWindw()
{
InitializeComponent();
MenuItems = new List<HouseInfo>();
HouseInfo houseInfo1 = new HouseInfo();
houseInfo1.House = "Header A";
houseInfo1.Details = new List<HouseInfo>() { new HouseInfo() { House = "Header A1" }, new HouseInfo() { House = "Header A2" } };
HouseInfo houseInfo2 = new HouseInfo();
houseInfo2.House = "Header B";
houseInfo2.Details = new List<HouseInfo>() { new HouseInfo() { House = "Header B1" }, new HouseInfo() { House = "Header B2" } };
MenuItems.Add(houseInfo1);
MenuItems.Add(houseInfo2);
DataContext = this;
}
}
public class HouseInfo
{
public string House
{
get;
set;
}
public List<HouseInfo> Details { get; set; }
private readonly ICommand _command;
public HouseInfo()
{
_command = new CommandViewModel(Execute);
}
public ICommand Command
{
get
{
return _command;
}
}
private void Execute()
{
// (NOTE: In a view model, you normally should not use MessageBox.Show()).
MessageBox.Show("Clicked at " + House);
}
}
public class CommandViewModel : ICommand
{
private readonly Action _action;
public CommandViewModel(Action action)
{
_action = action;
}
public void Execute(object o)
{
_action();
}
public bool CanExecute(object o)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { }
remove { }
}
}
}
you can gave style to every element with this code
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</Menu.ItemContainerStyle>
for example add this line to HouseInfo class
public Thickness Margin { get; set; }
and MainWindow.cs
MenuItems = new List<HouseInfo>();
HouseInfo houseInfo1 = new HouseInfo();
houseInfo1.House = "Header A";
houseInfo1.Margin = new Thickness(5);
houseInfo1.Details = new List<HouseInfo>() { new HouseInfo() { House = "Header A1" }, new HouseInfo() { House = "Header A2", Margin=new Thickness(10) } };
and set Style in xaml
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="Margin" Value="{Binding Margin}" />
</Style>
</Menu.ItemContainerStyle>
test:
I'm using Visual Studio 2015, I'm trying to teach myself the MVVM pattern, and I'm hitting a road block. My code is loosely based off of Josh Smiths article, I'm using it to help me learn MVVM and create a small app for work in the process.
What I'm trying to accomplish:
I've bound a viewmodel to a listview showing a list of products, each product has a list of "productTemplate" items. In my View I would like this list to populate inside a listbox when a product from my list view is selected. I am implementing INotifyPropertyChanged. I think I'm just missing something simple but I'm not sure.
My code:
Two Models (Product, ProductTemplateItem);
public class Product {
private string _productNum;
private string _productFamily;
public Product() {
}
public string ProductNum { get; set; }
public string ProductFamily { get; set; }
}
public class ProductTemplateItem : ChangeEventHandlerBase {
private string _TemplateItem;
private string _TemplateCode;
public ProductTemplateItem(string templateItem, string templateCode) {
_TemplateItem = templateItem;
_TemplateCode = templateCode;
}
public string TemplateItem {
get { return _TemplateItem; }
set { if(_TemplateItem != value) {
_TemplateItem = value;
OnPropertyChanged("TemplateItem");
}
}
}
public string TemplateCode {
get { return _TemplateCode; }
set {
if (_TemplateCode != value) {
_TemplateCode = value;
OnPropertyChanged("TemplateCode");
}
}
}
public override string DisplayName {
get {
return $"{TemplateItem} - {TemplateCode}";
}
}
}
My ViewModels (Product View Model, brings everything together and adds the product template list, and AlProductsViewModel adds data and exposes everything to be bound in XAML):
public class ProductViewModel : BaseViewModel {
private Product _product;
private bool _isSelected;
private List<ProductTemplateItem> _productTemplate;
public ProductViewModel(string productNum, string productFamily) {
Product.ProductNum = productNum;
Product.ProductFamily = productFamily;
_productTemplate = new List<ProductTemplateItem>();
}
public string ProductNumber {
get { return _product.ProductNum; }
set { if(_product.ProductNum != value) {
_product.ProductNum = value;
OnPropertyChanged("ProductNumber");
}
}
}
public string ProductFamily {
get { return _product.ProductFamily; }
set {
if (_product.ProductFamily != value) {
_product.ProductFamily = value;
OnPropertyChanged("ProductFamily");
}
}
}
public bool IsSelected {
get { return _isSelected; }
set {
if (_isSelected != value) {
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
public List<ProductTemplateItem> ProductTemplate {
get { return _productTemplate; }
set { if (_productTemplate != value) {
_productTemplate = value;
OnPropertyChanged("ProductTemplate");
}
}
}
public Product Product {
get {
if (_product == null) {
_product = new Product();
return _product;
}
else {
return _product;
}
}
set { if(_product != value) {
_product = value;
OnPropertyChanged("Product");
}
}
}
public override string DisplayName {
get {
return Product.ProductNum;
}
}
}
public class AllProductsViewModel : BaseViewModel{
public AllProductsViewModel() {
AddProducts();
}
private void AddProducts() {
List<ProductViewModel> all = new List<ProductViewModel>();
all.Add(new ProductViewModel("4835", "Crop Cart"));
all.Add(new ProductViewModel("780", "Piler"));
all.Add(new ProductViewModel("880", "Piler"));
all.Add(new ProductViewModel("150", "Scooper"));
all[0].ProductTemplate.Add(new Model.ProductTemplateItem("Miscellaneous","MISC"));
all[0].ProductTemplate.Add(new Model.ProductTemplateItem("Drawbar", "DRBR"));
all[0].ProductTemplate.Add(new Model.ProductTemplateItem("Mainframe", "FRAM"));
all[0].ProductTemplate.Add(new Model.ProductTemplateItem("Conveyor", "CONV"));
all[1].ProductTemplate.Add(new Model.ProductTemplateItem("Hello", "HELL"));
AllProducts = new ObservableCollection<ProductViewModel>(all);
}
public ObservableCollection<ProductViewModel> AllProducts { get; private set; }
}
And my XAML code which is a user control with a ListView based off of Josh's code and a listbox that needs to be updated based off of the selection in the ListView:
<UserControl x:Class="Parts_Book_Tool.Views.ProductsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Parts_Book_Tool.Views"
xmlns:viewModel="clr-namespace:Parts_Book_Tool.ViewModel"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<viewModel:AllProductsViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<CollectionViewSource x:Key="ProductGroups" Source="{Binding Path=AllProducts}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="ProductFamily"/>
</CollectionViewSource.GroupDescriptions>
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="ProductFamily" Direction="Ascending"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<GroupStyle x:Key="ProductGroupStyle">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock
FontWeight="Bold"
Margin="1"
Padding="4,2,0,2"
Text="{Binding Path=Name}"
/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
<Style x:Key="MainHCCStyle" TargetType="{x:Type HeaderedContentControl}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Border
Background="{StaticResource Brush_HeaderBackground}"
BorderBrush="LightGray"
BorderThickness="1"
CornerRadius="5"
Margin="4"
Padding="4"
SnapsToDevicePixels="True"
>
<TextBlock
FontSize="14"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"
Text="{TemplateBinding Content}"
/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ProductItemsStyle" TargetType="{x:Type ListViewItem}">
<!--
Stretch the content of each cell so that we can
right-align text in the Total Sales column.
-->
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<!--
Bind the IsSelected property of a ListViewItem to the
IsSelected property of a CustomerViewModel object.
-->
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="ItemsControl.AlternationIndex" Value="1" />
<Condition Property="IsSelected" Value="False" />
<Condition Property="IsMouseOver" Value="False" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="#EEEEEEEE" />
</MultiTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<HeaderedContentControl Header="Model Info" Style="{StaticResource MainHCCStyle}" Grid.Row="0">
<ListView x:Name="lvModelNumbers" Margin="6,2,6,50" DataContext="{StaticResource ProductGroups}"
ItemContainerStyle="{StaticResource ProductItemsStyle}" ItemsSource="{Binding}" >
<ListView.GroupStyle>
<StaticResourceExtension ResourceKey="ProductGroupStyle"/>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Model Number" Width="100" DisplayMemberBinding="{Binding Path=DisplayName}"/>
</GridView>
</ListView.View>
</ListView>
</HeaderedContentControl>
<HeaderedContentControl Header="Model Template" Style="{StaticResource MainHCCStyle}" Grid.Row="1">
<ListBox ItemsSource="{Binding SelectedItem/ProductTemplate, ElementName=lvModelNumbers}" Margin="6,2,6,50">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</HeaderedContentControl>
</Grid>
It feels to me like I'm missing the capture of an event to update the listbox, but with my inexperience in MVVM I Can't be sure. I've tried binding to the SelectedItem of the named element but that doesn't work. I can get the listbox to populate if I bind "AllProducts/ProductTemplate", but it just gives me the first indexed values, and doesn't dynamically change when I select another product.
Hopefully that is enough information, and any help would be greatly appreciated. I'm enjoying learning MVVM but it's dificult to wrap my head around.
Thanks,
Try bind the ListBox to the ProductTemplate property of the SelectedItem property of the ListView:
<ListView x:Name="lvModelNumbers" Margin="6,2,6,50"
ItemsSource="{Binding Source={StaticResource ProductGroups}}"
ItemContainerStyle="{StaticResource ProductItemsStyle}">
<ListView.GroupStyle>
<StaticResourceExtension ResourceKey="ProductGroupStyle"/>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Model Number" Width="100" DisplayMemberBinding="{Binding Path=DisplayName}"/>
</GridView>
</ListView.View>
</ListView>
<ListBox ItemsSource="{Binding SelectedItem.ProductTemplate, ElementName=lvModelNumbers}" Margin="6,2,6,50">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have a Toolbox (Visual Studio alike) and a textbox where user can filter. The filter is working however the items in the ListBox always remain not expanded.
View.xaml
<ListBox x:Name="ListBox" Grid.Row="1" ItemsSource="{Binding Items}"
PreviewMouseLeftButtonDown="OnListBoxPreviewMouseLeftButtonDown" MouseMove="OnListBoxMouseMove"
Background="Transparent" BorderThickness="0" ScrollViewer.CanContentScroll="False">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<gem:ExpanderEx Header="{Binding Name}" IsExpanded="{Binding IsSelected, Mode=TwoWay}">
<ItemsPresenter />
</gem:ExpanderEx>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontWeight="Bold" FontSize="15"
Text="{Binding Path=Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="18 0 0 0" ToolTip="{Binding Help}">
<Image Source="{Binding IconSource, Converter={StaticResource NullableValueConverter}}" Margin="0 0 5 0" Width="16" />
<TextBlock Text="{Binding Name}" Padding="2" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ViewModel.cs
items = new BindableCollection<ToolboxItemViewModel>();
collectionView = CollectionViewSource.GetDefaultView(items);
collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
collectionView.GroupDescriptions.Add(new PropertyGroupDescription("SubCategory"));
collectionView.Filter = ToolBoxFilter;
private readonly BindableCollection<ToolboxItemViewModel> items;
public IObservableCollection<ToolboxItemViewModel> Items { get { return items; } }
private string searchTerm;
public string SearchTerm
{
get { return searchTerm; }
set
{
if (searchTerm == value)
return;
searchTerm = value;
NotifyOfPropertyChange(() => SearchTerm);
//TODO: implement a defer here (Rx Extensions might help)
collectionView.Refresh(); //Refresh the filter.
}
}
ToolBoxItem.cs
public class ToolboxItemViewModel : PropertyChangedBase
{
private readonly ToolboxItem model;
public ToolboxItemViewModel(ToolboxItem model)
{
this.model = model;
IsSelected = false;
}
public ToolboxItem Model
{
get { return model; }
}
public string Name
{
get { return model.Name; }
}
public string Category
{
get { return model.Category; }
}
public string SubCategory
{
get { return model.SubCategory; }
}
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
NotifyOfPropertyChange(() => IsSelected);
}
}
How can I expand the grouped items when filtering?
Or another approach, how to expand the group item if there is any ListboxItem selected inside.
EDIT
Trying to figure it out, turns out I have a binding error on gem:ExpanderEx IsExpanded="{Binding IsSelected}"
BindingExpression path error 'IsSelected' property not found on 'object' 'CollectionViewGroupInternal'
In your collectionView filter, add yourObject.IsSelected = true;.
When you return true in your filter, you can alter the properties from your ToolboxItemViewModel object.
bool ToolBoxFilter(object item)
{
if (Search Criteria Match)
{
ToolboxItemViewModel model = (ToolboxItemViewModel)item;
model.IsSelected = true;
... rest of your code
}
}
I'm building a treeview with HierarchicalDataTemplates and would like to have some nodes expaned. The Nodes have a property "IsNodeExpanded" that I'd like to bind the property IsExpanded to.
Where I run into troubles is binding to this property. E.g. this
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded"
Value="{Binding DataContext.IsNodeExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}">
</Setter>
</Style>
will bind to a property IsNodeExpanded defined on my MainViewModel while this
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded"
Value="{Binding IsNodeExpanded}">
</Setter>
</Style>
will have no effect at all because, I guess, the binding has a datacontext problem.
How can I refer to the right datacontext?
For completeness, here is my TreeView
<TreeView ItemsSource="{Binding _questions}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Question}"
ItemsSource="{Binding Converter={StaticResource QuestionConverter}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MainOption}"
ItemsSource="{Binding MainOptions}">
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Path=Name}"
IsChecked="{Binding Path=IsSelected}"
Command="{Binding DataContext.ToggleSelectedMetaItem, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding Path=MetaItemId}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded"
Value="{Binding DataContext.IsNodeExpanded, RelativeSource={RelativeSource FindAncestor, AncestorType=TreeView}}">
</Setter>
</Style>
</TreeView.ItemContainerStyle>
EDIT:
public class MainOption
{
public string Name { get; set; }
public int MetaItemId { get; set; }
public bool IsSelected { get; set; }
public MainOption()
{
this.IsSelected = true;
}
}
public class Question
{
public string Name { get; set; }
public List<MainOption> MainOptions { get; set; }
public Question()
{
MainOptions = new List<MainOption>();
}
}
In ItemContainerStyle you should not define relative source when binding to IsNodeExpanded property. if itemssource _questions is list of Question, then {Binding IsNodeExpanded} binds to IsNodeExpanded property of Question class. I quess you are missing the property.
here is xaml:
<TreeView ItemsSource="{Binding Questions}">
<TreeView.Resources>
<DataTemplate x:Key="OptionTemplate" DataType="l:MainOption">
<CheckBox IsChecked="{Binding IsSelected}" Content="{Binding Name}" />
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="l:Question" ItemsSource="{Binding MainOptions}"
ItemTemplate="{StaticResource OptionTemplate}" ItemContainerStyle="{x:Null}">
<!-- DataContext of type Question -->
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
and C# code:
public class MainWindowViewModel : BindableBase
{
public MainWindowViewModel()
{
Questions = Enumerable.Range(1, 10)
.Select(i => new Question
{
Name = "Question " + i,
MainOptions = Enumerable.Range(1, 5)
.Select(j => new MainOption {Name = "Option " + i})
.ToList()
})
.ToList();
}
public List<Question> Questions { get; private set; }
}
public class Question
{
public string Name { get; set; }
public List<MainOption> MainOptions { get; set; }
public bool IsNodeExpanded { get; set; }
public Question()
{
IsNodeExpanded = true;
MainOptions = new List<MainOption>();
}
}
the point is, that you set Style and ItemTemplate of root items in threeview. In HierarchicalItemTemplate ItemSource is used to generate subitems, ItemContaierStyle affects style of subitems and ItemTemplate affects datatempalte of subitems
I have Listbox in my WP7 page like this
<ListBox Name="lstSelectedNumber" Height="50" MaxHeight="120" VerticalAlignment="Top" Grid.Column="1" SelectionChanged="lstSelectedNumber_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="-15" />
<Setter Property="Margin" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel>
</toolkit:WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<toolkit:AutoCompleteBox x:Name="acBox" FilterMode="StartsWith" ValueMemberBinding="{Binding Name,Mode=TwoWay}">
<toolkit:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Stretch="None" Margin="0,0,5,5"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</toolkit:AutoCompleteBox.ItemTemplate>
</toolkit:AutoCompleteBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My Listbox is attached to my collection List<SampleData>
Like lstSelectedNumber.itemsource = List<SampleData>;
And for my autocompletebox I want to bind my auto completebox to other collection List so when the user type in my textbox it shows suggestion to user and once user select to any item it adds this item to my other collection List
I am facing one problem: how can I bind my list to the autocompletbox which in the listbox so I can do further proceedings?
UPDATE
I am trying to find my listbox control in this way but its returning me 0 always for childs
private void SearchVisualTree(DependencyObject targetElement)
{
var count = VisualTreeHelper.GetChildrenCount(targetElement);
if (count == 0)
return;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(targetElement, i);
if (child is AutoCompleteBox)
{
AutoCompleteBox myItems = (AutoCompleteBox)child;
if (myItems.Name == "acBox")
{
// My Logic
return;
}
}
else
{
SearchVisualTree(child);
}
}
}
and in this way I'm calling on my page constructor
SearchVisualTree(this.lstSelectedNumber);
List<WP7Phone> data = new List<WP7Phone>();
foreach (lines l in result)
{
WP7Phone w7 = new WP7Phone();
w7.Name = l.line.TrimStart();
w7.Image = "images/thump.jpg";
msg.Add(w7);
}
this.autoCompleteBox1.ItemsSource = data;
public class WP7Phone
{
public string Name
{
get;
set;
}
public string Image1
{
get;
set;
}
}