How to databind to a ItemsControl - wpf

I have a SL5 project where I am trying to data bind a collection to a ItemsControl, I keep getting the error:
BindingExpression path error: 'ItemName' property not found on 'EventViewer.Data.ViewModels.ProductListModel' 'EventViewer.Data.ViewModels.ProductListModel' (HashCode=7414170). BindingExpression: Path='ItemName' DataItem='EventViewer.Data.ViewModels.ProductListModel' (HashCode=7414170); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
BindingExpression path error: 'Price' property not found on 'EventViewer.Data.ViewModels.ProductListModel' 'EventViewer.Data.ViewModels.ProductListModel' (HashCode=7414170). BindingExpression: Path='Price' DataItem='EventViewer.Data.ViewModels.ProductListModel' (HashCode=7414170); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
BindingExpression path error: 'Description' property not found on 'EventViewer.Data.ViewModels.ProductListModel' 'EventViewer.Data.ViewModels.ProductListModel' (HashCode=7414170). BindingExpression: Path='Description' DataItem='EventViewer.Data.ViewModels.ProductListModel' (HashCode=7414170); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
I have these properties in the ProductQtyItem and the databinding appears to be setup correctly. Here is what I have:
The XAML:
<sdk:ChildWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:EventViewer="clr-namespace:EventViewer"
xmlns:ViewModels="clr-namespace:EventViewer.Data.ViewModels"
xmlns:converters="clr-namespace:EventViewer.Converters"
x:Class="EventViewer.PurchaseWindow"
Title="Purchase"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="539" Height="550">
<sdk:ChildWindow.Resources>
<converters:BooleanVisibilityConverter x:Key="BooleanVisibilityConverter"/>
<converters:NumericCurrencyConverter x:Key="NumericCurrencyConverter"/>
<ControlTemplate x:Key="ProductItemTemplate" TargetType="ItemsControl">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" VerticalAlignment="Center"
Style="{StaticResource ProductNameTextBlockStyle}"
Text="{Binding ItemName, Mode=OneWay}"/>
<TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Center"
Style="{StaticResource ProductPriceTextBlockStyle}"
Text="{Binding Price, Converter={StaticResource NumericCurrencyConverter}, Mode=OneWay}" />
<TextBlock Grid.Column="1" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center" TextWrapping="Wrap"
Text="{Binding Description, Mode=OneWay}"/>
</Grid>
</ControlTemplate>
</sdk:ChildWindow.Resources>
<Grid x:Name="LayoutRoot">
<Grid.DataContext>
<ViewModels:ProductListModel/>
</Grid.DataContext>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Viewbox Stretch="Uniform" StretchDirection="DownOnly" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="100"
DataContext="{Binding ImageData}">
<Image Source="{Binding ImagePath, Mode=OneWay}" ImageOpened="Image_ImageOpened"/>
</Viewbox>
<ItemsControl
HorizontalAlignment="Left" Margin="0,105,0,0" VerticalAlignment="Top" Width="521" Height="377"
Template="{StaticResource ProductItemTemplate}"
ItemsSource="{Binding ProductQtyItems}"/>
<Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Grid.Row="1" />
</Grid>
</sdk:ChildWindow>
The code that is to follow is how I wire up a ProductListModel to the LayoutRoot.DataContext of the above XAML code. So, if I understand things correctly, the binding for the ItemsControl will bind to the List ProductQtyItems of ProductListModel. So, won't the templates have the datasource set to ProductQtyItem? What am I missing?
Here is the rest of the code:
And this is the code that populates the data binding:
public static void Display(FrameworkElement parent, ImageData imageData)
{
var purchaseWindow = new PurchaseWindow();
var productQtyItems = new List<ProductQtyItem>();
foreach (var p in SystemSettings.GetInstance().ProductList.Collection)
{
int qty = 0;
imageData.ProductListItemQty.TryGetValue(p.Id, out qty);
productQtyItems.Add(ProductQtyItem.Create(p, qty));
}
purchaseWindow.LayoutRoot.DataContext = new ProductListModel
{
ImageData = imageData,
ProductQtyItems = productQtyItems
};
purchaseWindow.Show();
}
Here is the Model that is being set to the LayoutRoot.Datacontext:
public class ProductListModel : PropertyChangedBase
{
private List<ProductQtyItem> _productQtyItems;
private ImageData _imageData;
public List<ProductQtyItem> ProductQtyItems
{
get { return _productQtyItems; }
set { _productQtyItems = value; }
}
public ImageData ImageData
{
get { return _imageData; }
set { _imageData = value; }
}
}
And finally the ProductQtyItem:
public class ProductQtyItem : PropertyChangedBase
{
public static ProductQtyItem Create(ProductItem productItem, int qty)
{
return new ProductQtyItem
{
_productItem = productItem,
_qty = qty,
};
}
private ProductItem _productItem;
private int _qty;
public int Id { get { return _productItem.Id; } }
public int SortOrder { get { return _productItem.SortOrder; } }
public string ItemName { get { return _productItem.ItemName; } }
public string Description { get { return _productItem.Description; } }
public double Price { get { return _productItem.Price; } }
public bool IsQtyEnabled { get { return _productItem.IsQtyEnabled; } }
public int Qty
{
get { return _qty; }
set
{
if (value != _qty)
{
_qty = value;
NotifyPropertyChanged("Qty");
}
}
}
public override bool Equals(object obj)
{
var other = obj as ProductQtyItem;
if (other == null)
return false;
return Id == other.Id;
}
public override int GetHashCode()
{
return Id;
}
}

The error message indicates that DataContext of controls that bound to properties ItemName, Price, Description is ProductListModel instead of ProductQtyItem.
Which model/viewmodel has property named Collection and of what type is Collection? I think the problem is here, if it is of type ProductListModel.
UPDATE :
Responding to your update, try to change resource definition from ControlTemplate :
<ControlTemplate x:Key="ProductItemTemplate" TargetType="ItemsControl">..</ControlTemplate>
to DataTemplate :
<DataTemplate x:Key="ProductItemTemplate">..</DataTemplate>
then bind ItemsControl's ItemTemplate property instead of Template property :
<ItemsControl
HorizontalAlignment="Left" Margin="0,105,0,0" VerticalAlignment="Top" Width="521" Height="377"
ItemTemplate="{StaticResource ProductItemTemplate}"
ItemsSource="{Binding ProductQtyItems}"/>
PS : actually there are too many information posted, and still not directly reproduceable, because some classes definition still missing. Next time try to simplify the scenario using reduced viewmodel, remove unrelated resources (styles, converters), but make sure the problem still appears.

Related

WPF, RichTextBox, SelectionBrush, not SelectionBackColor

In a WPF app RichTextBox, I'm trying to find a way to provide a background color for various words within the box. In the System.Windows.Forms version of the RichTextBox, there was a very simple way to do this:
richTextBox1.SelectionBackColor = color;
richTextBox1.AppendText(word);
However, the System.Windows.Controls version of RichTextBox only has SelectionBrush, and this same method does not work.
Is a background color for different words in the RichTextBox possible?
You can work with the FlowDocument within the RichTextBox
<RichTextBox>
<RichTextBox.Document>
<FlowDocument>
<Paragraph>
<Run Background="Red">Hello World</Run>
<LineBreak/>
<Run Background="Green">This is a colored</Run>
<Run>text.</Run>
</Paragraph>
</FlowDocument>
</RichTextBox.Document>
</RichTextBox>
Edit regarding your comments: a (nearly) full example showing two different approaches.
No matter what you end up doing to present your text in the UI, you should have some sort of logic that creates a suitable data model of your highlighted text. The following example uses a collection of TextFragment where each fragment is optionally marked as highlighted.
public class TextFragment
{
public TextFragment(string text, bool isHighlighted)
{
this.Text = text;
this.IsHighlighted = isHighlighted;
}
public string Text { get; private set; }
public bool IsHighlighted { get; private set; }
}
Also, for the sample I use a class TextEntry to manage original text, search text and the resulting text fragments. Note I inherit from a BaseViewModel class which implements some helper functions for INotifyPropertyChanged related things. The helper function bool SetProperty<T>(ref T store, T value, [CallerMemberName]string propertyName = null) will check whether value and store are equal, potentially update the store with value and raise a property changed notification. The return value indicates, whether the value was really different/changed.
public class TextEntry : BaseViewModel
{
public TextEntry()
{
TextParts = new ObservableCollection<TextFragment>();
}
private void UpdateTextParts()
{
TextParts.Clear();
if (string.IsNullOrEmpty(SearchText))
{
TextParts.Add(new TextFragment(OriginalText, false));
return;
}
int startAt = 0;
do
{
int next = OriginalText.IndexOf(SearchText, startAt, StringComparison.CurrentCultureIgnoreCase);
if (next == -1)
{
TextParts.Add(new TextFragment(OriginalText.Substring(startAt), false));
return;
}
else
{
if (next != startAt)
{
TextParts.Add(new TextFragment(OriginalText.Substring(startAt, next - startAt), false));
}
// add highlighted part
TextParts.Add(new TextFragment(OriginalText.Substring(next, SearchText.Length), true));
startAt = next + SearchText.Length;
}
} while (startAt < OriginalText.Length);
}
private string _OriginalText;
public string OriginalText
{
get { return _OriginalText; }
set
{
if (SetProperty(ref _OriginalText, value))
{
UpdateTextParts();
}
}
}
private string _SearchText;
public string SearchText
{
get { return _SearchText; }
set
{
if (SetProperty(ref _SearchText, value))
{
UpdateTextParts();
}
}
}
public ObservableCollection<TextFragment> TextParts { get; private set; }
}
You can create a multi-part text in the UI by appending multiple textblocks with different text settings in a horizontal StackPanel. This way, the text parts can be managed by an ItemsControl. Alternatively, you can use the RichTextBox with its Document property, but this needs some more handling in code behind.
Some initialization code in the main window and a method to update the document for the RichTextBox example:
public MainWindow()
{
InitializeComponent();
var vm = new TextEntry();
grid1.DataContext = vm;
// this trigger works, but don't ask about efficiency for a bigger application
vm.TextParts.CollectionChanged += TextParts_CollectionChanged;
}
void TextParts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ObservableCollection<TextFragment> data = sender as ObservableCollection<TextFragment>;
var doc = richTextBox1.Document;
var paragraph = new Paragraph();
paragraph.Inlines.AddRange(data.Select(x =>
{
var run = new Run(x.Text);
if (x.IsHighlighted)
{
run.Background = Brushes.LightCoral;
}
return run;
}));
doc.Blocks.Clear();
doc.Blocks.Add(paragraph);
}
And the XAML content of the window:
<Grid x:Name="grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Original Text: " Margin="3"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Searched Word: " Margin="3"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Highlighted Text: " Margin="3"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Highlighted Text2: " Margin="3"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding OriginalText, UpdateSourceTrigger=PropertyChanged}" Margin="3"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding SearchText,UpdateSourceTrigger=PropertyChanged}" Margin="3"/>
<ItemsControl Grid.Row="2" Grid.Column="1" ItemsSource="{Binding TextParts}" Margin="3" IsTabStop="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
<Setter Property="Background" Value="LightCoral"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<RichTextBox Grid.Row="3" Grid.Column="1" x:Name="richTextBox1" Margin="3" VerticalAlignment="Top" IsReadOnly="True"/>
</Grid>
Expected Program:
Two lines with text inputs. One for the original text, one for the searched text.
3rd line showing the original text with search highlights as TextBlocks.
4th line showing the original text with search highlights as RichTextBox.

WPF AutocompeteBox in datagrid Cell does not work properly

I am testing the WPF AutoCompleteBox control in datagrid cell.
I met two problems:
1) when i navigate to the autocomplete cell , it does not automatically switch to edit mode,
2) When I switch into edit mode and I type something, the list of suggesstions doesn’t appears and I after closing the window, i have a debug error that says :
System.Windows.Data Error: 40 : BindingExpression path error: 'Names' property not found on 'object' ''Person' (HashCode=40808136)'. BindingExpression:Path=Names; DataItem='Person' (HashCode=40808136); target element is 'AutoCompleteBox' (Name='acb2'); target property is 'ItemsSource' (type 'IEnumerable')
Here The code
namespace WpfPlayingWithDatagrid
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyViewModel mv = new MyViewModel();
this.DataContext = mv;
}
}
}
public class MyViewModel : ObservableObject
{
ObservableCollection<Person> _names = null;
RelayCommand _loadClients;
RelayCommand _showSelectedPerson;
Person _selectedPerson;
public Person SelectedPerson
{
get { return _selectedPerson; }
set { _selectedPerson = value; }
}
public ObservableCollection<Person> Names
{
get { return _names; }
set { _names = value;
RaisePropertyChanged("Names");
}
}
public RelayCommand LoadClientCommand
{
get
{
if (_loadClients == null)
_loadClients = new RelayCommand(LoadCommandExecute);
return _loadClients;
}
}
private void LoadCommandExecute()
{
LoadClients();
}
public void LoadClients()
{
List<Person> ll = new List<Person>(5);
ll.Add(new Person(1,"ETS CUSTOMER1","Addresse1"));
ll.Add(new Person(2,"COMPX CUSTOMER2","Addresse 2"));
ll.Add(new Person(3,"ENTREPRISE3","Adresse3"));
ll.Add(new Person(4,"SOCIETE X4HERTZ","Addresse4"));
ll.Add(new Person(5,"CARCOMP","Addresse5"));
Names = new ObservableCollection<Person>(ll);
}
public RelayCommand ShowSelectedPersonCommand
{
get
{
if (_showSelectedPerson == null)
_showSelectedPerson = new RelayCommand(ShowSelectedPersonCommandExecute);
return _showSelectedPerson;
}
}
private void ShowSelectedPersonCommandExecute()
{
if (SelectedPerson != null)
MessageBox.Show(SelectedPerson.Nom);
else
MessageBox.Show("No selection.");
}
}}
and The XAML is as follows :
<Window x:Class="WpfPlayingWithDatagrid.MainWindow"
x:Name="wnd"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:gs="http://www.galasoft.ch/mvvmlight"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
xmlns:local="clr-namespace:WpfPlayingWithDatagrid"
Title="MainWindow" >
<Window.Resources>
<local:MyViewModel x:Key="MyViewModel"/>
<Style x:Key="acbStyle" TargetType="controls:AutoCompleteBox">
<Setter Property="FilterMode" Value="Contains"/>
<Setter Property="IsTextCompletionEnabled" Value="True"/>
</Style>
<DataTemplate x:Key="AutoCompleteBoxItemTemplate">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Code}" Width="20" />
<Label Content="{Binding Nom}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Column="1"
Content="Load Customers"
Command="{Binding LoadClientCommand}" Margin="10"/>
<DataGrid Grid.Row="1"
Grid.ColumnSpan="3"
AutoGenerateColumns="False"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
RowHeight="30"
Grid.Column="0"
SelectionUnit="Cell"
ItemsSource="{Binding Names,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Grid.RowSpan="2"
>
<DataGrid.Columns>
<DataGridTextColumn
Binding="{Binding Code, Mode=TwoWay, StringFormat=\{0:#\}}" Header="Code" />
<DataGridTemplateColumn Header="Name" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Nom}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<controls:AutoCompleteBox
x:Name="acb2"
Text="{Binding Nom}"
ItemsSource="{Binding Names}"
ValueMemberBinding="{Binding Nom}"
Style="{StaticResource acbStyle}"
ItemTemplate="{StaticResource AutoCompleteBoxItemTemplate}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding Adresse, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Adresse" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
and Person class :
namespace WpfPlayingWithDatagrid
{
public class Person
{
int code;
public int Code
{
get { return code; }
set { code = value; }
}
string nom;
public string Nom
{
get { return nom; }
set { nom = value; }
}
string adresse;
public string Adresse
{
get { return adresse; }
set { adresse = value; }
}
public Person(int c, string n, string a)
{
Code = c;
Nom = n;
Adresse = a;
}
}
}
Thank you in advance.
Due to the way DataGridColumns are implemented, binding to parent viewmodels are always problematic.
The reason you are getting the binding error is because the row is bound to Person, and Person does not have the Names property.
The names property occur on MyViewModel and can be accessed like this
<controls:AutoCompleteBox
x:Name="acb2"
Text="{Binding Nom}"
ItemsSource="{Binding Names,Source={StaticResource MyViewModel}}"
ValueMemberBinding="{Binding Nom}"
Style="{StaticResource acbStyle}"
ItemTemplate="{StaticResource AutoCompleteBoxItemTemplate}"
/>
Updated
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyViewModel mv = (MyViewModel) FindResource("MyViewModel");
this.DataContext = mv;
}
}

Binding does not work for TemplateSelector

I have one part of the view is dynamic based on a TemplateSelector. However, the binding does not work for the controls in the DataTemplate.(The controls do show on screen, just the conetent/texts are empty). I suspect it's a DataContext issue, but couldn't figure out after a lot of searching on line. Here is my XAML:
<Grid>
<Grid.DataContext>
<local:MyViewModel/>
</Grid.DataContext>
<Grid.Resources>
<DataTemplate x:Key="T1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Music"
Style="{StaticResource ResourceKey=textBlockStyle}" />
<TextBox Grid.Column="1"
Grid.Row="0"
Style="{StaticResource ResourceKey=textBoxStyle}"
Text="{Binding Path=MusicName}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="T2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Currency"
Style="{StaticResource ResourceKey=textBlockStyle}" />
<ComboBox Grid.Column="1"
Grid.Row="0"
Style="{StaticResource ResourceKey=comboBoxStyle}"
ItemsSource="{Binding Path=Currency_List}"
SelectedItem="{Binding Path=Currency}" />
</Grid>
</DataTemplate>
<local:ProductTypeTemplateSelector T1="{StaticResource ResourceKey=T1}"
T2="{StaticResource ResourceKey=T2}"
x:Key="myTemplateSelector" />
<Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
<RowDefinition Height="40"/>
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<!-- This biding works -->
<TextBlock Grid.Row="0"
Text="{Binding Path=MusicName}"/>
<!-- This biding does not work -->
<ContentControl Grid.Row="1"
Name="ccc"
Content="{Binding Path=Product_Type}"
ContentTemplateSelector="{StaticResource myTemplateSelector}">
</ContentControl>
</Grid>
This is my View Model (Technically, it is View Model and Model mixed together. I am not really implementing a full MVVM pattern)
public class MyViewModel: INotifyPropertyChanged
{
public MyViewModel()
{
SetLists();
}
protected void SetLists()
{
SetList_Product_Type();
SetList_Currency();
}
protected void SearchAndPopulate()
{
string query = string.Format("select * from dbo.vehicle_attributes where ticker like '%{0}%'", Search_Text);
DataView dv = DAL.ExecuteQuery(query);
if (dv.Count > 0)
{
DataRowView dvr = dv[0];
Vehicle_Id = int.Parse(dvr["vehicle_id"].ToString());
Product_Type = dvr["product_type_name"].ToString();
Vehicle_Name = dvr["vehicle_name"].ToString();
Is_Onshore = dvr["domicile_name"].ToString() == "Onshore";
Currency = dvr["currency"].ToString();
CUSIP = dvr["CUSIP"].ToString();
ISIN = dvr["isin"].ToString();
Ticker = dvr["ticker"].ToString();
Valoren = dvr["valoren"].ToString();
PC_Class = PC_Class_List.Find(x => x.Class_Name == dvr["class_name"].ToString());
Implementation_Type = Implementation_Type_List.Find ( x => x.Implementation_Type_Name == dvr["implementation_type_name"].ToString());
Price_Frequency = Price_Frequency_List.Find( x => x.Price_Frequency_Name == dvr["price_freq_name"].ToString());
Status = Status_List.Find( x => x.Status_Name == dvr["status_name"].ToString());
if (!string.IsNullOrEmpty(dvr["last_status_update"].ToString()))
{
Status_Date = DateTime.Parse(dvr["last_status_update"].ToString());
}
else
{
Status_Date = DateTime.MinValue;
}
switch (Product_Type)
{
case "Mutual Fund":
query = string.Format("select lf.dividend_currency, i.ticker from dbo.liquid_funds lf " +
"left join dbo.vehicles i on i.vehicle_id = lf.index_id " +
"where lf.vehicle_id ='{0}'",Vehicle_Id);
DataView dv_mutual_fund = DAL.ExecuteQuery(query);
if(dv_mutual_fund.Count > 0)
{
DataRowView dvr_mutual_fund = dv_mutual_fund[0];
Dividend_Currency = dvr_mutual_fund["dividend_currency"].ToString();
Benchmark_Ticker = dvr_mutual_fund["ticker"].ToString();
}
break;
default:
break;
}
}
}
public ICommand SearchVehicleCommand
{
get
{
return new Command.DelegateCommand(SearchAndPopulate);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
//ProductType
protected List<string> _product_Type_List = new List<string>();
public List<string> Product_Type_List
{
get
{
return _product_Type_List;
}
set
{
_product_Type_List = value;
OnPropertyChanged("Product_Type_List");
}
}
protected void SetList_Product_Type()
{
string query = "SELECT * FROM dbo.product_types WHERE is_enabled = 1";
DataView dv = DAL.ExecuteQuery(query);
List<string> l = new List<string>();
for (int i = 0; i < dv.Count; i++)
{
l.Add(dv[i]["product_type_name"].ToString());
}
Product_Type_List = l;
}
protected string _product_type;
public string Product_Type
{
get
{
return _product_type;
}
set
{
_product_type = value;
OnPropertyChanged("Product_Type");
SetList_Implementation_Type();
}
}
//Currency
protected List<string> _currency_List = new List<string>();
public List<string> Currency_List
{
get
{
return _currency_List;
}
set
{
_currency_List = value;
OnPropertyChanged("Currency_List");
}
}
protected void SetList_Currency()
{
string query = "SELECT currency FROM dbo.currencies";
DataView dv = DAL.ExecuteQuery(query);
List<string> l = new List<string>();
for (int i = 0; i < dv.Count; i++)
{
l.Add(dv[i]["currency"].ToString());
}
Currency_List = l;
}
protected string _currency;
public string Currency
{
get
{
return _currency;
}
set
{
_currency = value;
OnPropertyChanged("Currency");
}
}
// Music Name
protected string _musicName;
public string MusicName
{
get
{
return _musicName;
}
set
{
_musicName = value;
OnPropertyChanged("MusicName");
}
}
}
This is the class interface (sorry for the formatting above, but somehow I can't get it right):
And this is my DelegateCommand class:
public class DelegateCommand : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
}
This is the DataTemplateSelector:
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate T1 { get; set; }
public DataTemplate T2 { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
string product_type = (string)item;
if (product_type == "Type1")
return T1;
else
return T2;
}
}
The DataContext of a DataTemplate is set to the object that it is bound to. So in your case the DataContext for your Templates are Product_Type and you are expecting it to be MyViewModel.
There is a workaround for what you need. It uses a RelativeSource binding and FindAncester to access the DataContext of the Parent object.
Your DataTemplate bindings should look like this:
XAML
<DataTemplate x:Key="T1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Music"
Style="{StaticResource ResourceKey=textBlockStyle}" />
<TextBox Grid.Column="1"
Grid.Row="0"
Style="{StaticResource ResourceKey=textBoxStyle}"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentControl}}, Path=DataContext.MusicName}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="T2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Currency"
Style="{StaticResource ResourceKey=textBlockStyle}" />
<ComboBox Grid.Column="1"
Grid.Row="0"
Style="{StaticResource ResourceKey=comboBoxStyle}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentControl}}, Path=DataContext.Currency_List}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentControl}}, Path=DataContext.Currency}" />
</Grid>
</DataTemplate>
From MSDN
Find Ancestor - Refers to the ancestor in the parent chain of the data-bound element. You can use this to bind to an ancestor of a specific type or its subclasses.
The AncestorType attribute goes up the visual tree and finds the first Ancestor of the given type, in this case it's looking for ContentControl the path to the required property can then be set relative to this.
Update
Think of a template as a guide on how to display an object. The DataContext of the DataTemplate is going to be whatever object it is asked it to display.
In this case the ContentControl is told to display Product_Type and, depending on the value of Product_Type, to use a particular Template. Product_Type is given to the DataTemplate and becomes the DataContext.
WPFTutorials has some good examples.

WPF TreeView DataBinding: data not shown, BindingExpression path error

I'm working on a new functionality for Visual Studio Add-in. Initially the project's target framework was 3.5. But I was asked to add a Tool Window with quite complicated UI using WPF and switch to 4.0 framework (maybe this is important)
I'm trying to bind hierarchical data to the Tree View inside my Tool Window which is originally a WPF User Control.
But I see the following error:
"System.Windows.Data Error: 40 : BindingExpression path error: 'PoolList' property not found on 'object' ''OpjectPool' (Name='')'. BindingExpression:Path=PoolList; DataItem='OpjectPool' (Name=''); target element is 'TreeView' (Name='treeView1'); target property is 'ItemsSource' (type 'IEnumerable')"
This is a Class which represents data I need to bind to the tree view.
class CodeItem
{
public TextPoint StartPoint { get; set; }
public TextPoint EndPoint { get; set; }
public string Name { get; set; }
public vsCMElement Kind { get; set; }
public CodeElements ChildClasses { get; set; }
public ProjectItem ProjectItem { get; set; }
public List<CodeItem> CodeItems { get; set; }
public string Label { get; set; }
public CodeItem(CodeElement el)
{
StartPoint = el.StartPoint;
EndPoint = el.EndPoint;
Name = el.Name;
Kind = el.Kind;
ChildClasses = el.Children;
ProjectItem = el.ProjectItem;
Label = Kind.ToString();
CodeItems = new List<CodeItem>();
if (ChildClasses.Count != 0)
{
foreach (CodeElement elem in ChildClasses)
{
if (elem.Kind.ToString() == "vsCMElementClass")
{
CodeItems.Add(new CodeItem(elem));
}
}
}
}
}
Here is my UserControl code:
public partial class OpjectPool : UserControl
{
public ObservableCollection<CodeItem> PoolList = new ObservableCollection<CodeItem>();
public OpjectPool()
{
Project pr = ... // getting VS Project we want to work with;
foreach (ProjectItem item in pr.ProjectItems.Item("Objects").ProjectItems)
{
if (item.FileCodeModel != null)
{
CodeItem rootPoolItem = new CodeItem(item.Name);
foreach (CodeElement el in item.FileCodeModel.CodeElements)
{
if (el.Kind.ToString() == "vsCMElementClass" || el.Kind.ToString() == "vsCMElementNamespace")
{
CodeItem ci = new CodeItem(el);
rootPoolItem.CodeItems.Add(ci);
}
}
PoolList.Add(rootPoolItem);
}
}
InitializeComponent();
this.treeView1.DataContext = this;
}
}
And Here is a XAML code:
<UserControl x:Class="******.OpjectPool"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Height="Auto" Name="maingrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" Height="Auto" HorizontalAlignment="Stretch" Name="scrollViewer1" VerticalAlignment="Stretch" Width="Auto" HorizontalScrollBarVisibility="Visible">
<TreeView Height="Auto" Name="treeView1" Width="Auto" ItemsSource="{Binding PoolList}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding CodeItems}" >
<TreeViewItem Header="{Binding Label}"/>
</HierarchicalDataTemplate >
</TreeView.ItemTemplate>
</TreeView>
</ScrollViewer>
<ScrollViewer Grid.Column="1" Height="Auto" HorizontalAlignment="Stretch" Name="scrollViewer2" VerticalAlignment="Stretch" Width="Auto" HorizontalScrollBarVisibility="Visible" Grid.ColumnSpan="1">
<DataGrid AutoGenerateColumns="False" Height="Auto" Name="dataGrid1" Width="Auto" FrozenColumnCount="3" />
</ScrollViewer>
<ScrollViewer Grid.Column="2" Height="Auto" HorizontalAlignment="Stretch" Name="scrollViewer3" VerticalAlignment="Stretch" Width="Auto" HorizontalScrollBarVisibility="Visible" Grid.ColumnSpan="1">
<DataGrid AutoGenerateColumns="False" Height="Auto" Name="dataGrid2" Width="Auto" />
</ScrollViewer>
<GridSplitter Grid.Column="1" Name="gridSplitter1" ResizeDirection="Columns" BorderBrush="Black" Background="Black" Margin="0,0,0,0" Grid.ColumnSpan="1" HorizontalAlignment="Left" Width="2" />
<GridSplitter Grid.Column="2" Name="gridSplitter2" ResizeDirection="Columns" BorderBrush="Black" Background="Black" Margin="0,0,0,0" Grid.ColumnSpan="1" HorizontalAlignment="Left" Width="2" />
</Grid>
PoolList is not null and contains a full hierarchical structure I need to bind.
Interesting that error message says about real and not null property of the object
As error states:
BindingExpression path error: 'PoolList' PROPERTY not found on 'object' ''OpjectPool' (Name='')'. BindingExpression:Path=PoolList;
PoolList is not a property.

Horizontal stretch list item in WP7 with grouping

I want to implement ListBox Grouping on WP7. I found this article is very useful. Actually I made the grouping works. But I got a problem with ListItem horizontal stretch. I guess I need to set ItemContainerStyle and change HorizontalContentAlignment as Stretch. But it doesn't work for this case (if set the ItemTemplate directly, it works). Any suggestions? Thanks a lot!
Here is the code, the ListItem is supposed to be stretched, but it's centered instead.
C#:
public class GroupingItemsControlConverter : IValueConverter
{
public object Convert(object value, Type tagetType, object parameter, CultureInfo culture)
{
var valueAsIEnumerable = value as IEnumerable;
if (null == valueAsIEnumerable)
{
throw new ArgumentException("GroupingItemsControlConverter works for only IEnumerable inputs.", "value");
}
var parameterAsGroupingItemsControlConverterParameter = parameter as GroupingItemsControlConverterParameters;
if (null == parameterAsGroupingItemsControlConverterParameter)
{
throw new ArgumentException("Missing required GroupingItemsControlConverterParameter.", "parameter");
}
var groupSelectorAsIGroupingItemsControlConverterSelector = parameterAsGroupingItemsControlConverterParameter.GroupSelector as IGroupingItemsControlConverterSelector;
if (null == groupSelectorAsIGroupingItemsControlConverterSelector)
{
throw new ArgumentException("GroupingItemsControlConverterParameter.GroupSelector must be non-null and implement IGroupingItemsControlConverterSelector.", "parameter");
}
// Return the grouped results
return ConvertAndGroupSequence(valueAsIEnumerable.Cast<object>(), parameterAsGroupingItemsControlConverterParameter);
}
private IEnumerable<object> ConvertAndGroupSequence(IEnumerable<object> sequence, GroupingItemsControlConverterParameters parameters)
{
// Validate parameters
var groupKeySelector = ((IGroupingItemsControlConverterSelector)(parameters.GroupSelector)).GetGroupKeySelector();
var orderKeySelector = ((IGroupingItemsControlConverterSelector)(parameters.GroupSelector)).GetOrderKeySelector();
if (null == groupKeySelector)
{
throw new NotSupportedException("IGroupingItemsControlConverterSelector.GetGroupSelector must return a non-null value.");
}
// Do the grouping and ordering
var groupedOrderedSequence = sequence.GroupBy(groupKeySelector).OrderBy(orderKeySelector);
// Return the wrapped results
foreach (var group in groupedOrderedSequence)
{
yield return new ContentControl { Content = group.Key, ContentTemplate = parameters.GroupStyle };
foreach (var item in group)
{
yield return new ContentControl { Content = item, ContentTemplate = parameters.ItemStyle };
}
}
}
public object ConvertBack(object value, Type tagetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException("GroupingItemsControlConverter does not support ConvertBack.");
}
}
public class GroupingItemsControlConverterParameters
{
public DataTemplate GroupStyle { get; set; }
public DataTemplate ItemStyle { get; set; }
public IGroupingItemsControlConverterSelector GroupSelector { get; set; }
};
public abstract class IGroupingItemsControlConverterSelector
{
public abstract Func<object, IComparable> GetGroupKeySelector();
public virtual Func<IGrouping<IComparable, object>, IComparable> GetOrderKeySelector() { return g => g.Key; }
}
public class GroupingItemsControlConverterSelector : IGroupingItemsControlConverterSelector
{
public override Func<object, IComparable> GetGroupKeySelector()
{
return (o) => (o as ItemViewModel).Group;
}
}
XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.Resources>
<DataTemplate x:Key="GroupHeaderTemplate">
<Border BorderBrush="Yellow" BorderThickness="1" Margin="12,3,12,12" Padding="6" VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Number}" HorizontalAlignment="Left" Margin="6,0,0,0" FontSize="22" Foreground="White"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="22" Foreground="White"/>
</Grid>
</Border>
</DataTemplate>
<DataTemplate x:Key="CustomItemTemplate">
<Grid Margin="12,3,12,12" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding Number}" HorizontalAlignment="Left" Margin="6,0,0,0" FontSize="22" Foreground="White"/>
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Name}" HorizontalAlignment="Right" Margin="0,0,6,0" FontSize="22" Foreground="White"/>
</Grid>
</DataTemplate>
<local:GroupingItemsControlConverter x:Key="GroupingItemsConverter" />
<local:GroupingItemsControlConverterSelector x:Key="GroupingItemsSelector" />
<local:GroupingItemsControlConverterParameters x:Key="GroupingItemParameters"
GroupStyle="{StaticResource GroupHeaderTemplate}"
ItemStyle="{StaticResource CustomItemTemplate}"
GroupSelector="{StaticResource GroupingItemsSelector}"
/>
<Style TargetType="ListBoxItem" x:Key="CustomItemContainerStyle">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</Grid.Resources>
<ListBox x:Name="TheListBox"
ItemsSource="{Binding Items, Converter={StaticResource GroupingItemsConverter}, ConverterParameter={StaticResource GroupingItemParameters}}"
ItemContainerStyle="{StaticResource CustomItemContainerStyle}" />
</Grid>
ListBox grouping? You should consider using the LongListSelector from the Silverlight Toolkit. And to simplify the binding for that, you can use the LongListCollection collection type (Check the entire example, for details).
Then you can simply create apps that groups values, for example like this:

Resources