I have 3 TextBoxes bind with my class(Transaction) properties like this
<TextBox Text="{Binding Path=Transaction.Bills100,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="bills100" Grid.Column="2" Grid.Row="1" Margin="7"></TextBox>
<TextBox Text="{Binding Path=Transaction.Bill50,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="bills50" Grid.Column="2" Grid.Row="2" Margin="7"></TextBox>
<TextBox Text="{Binding Path=Transaction.Bill20,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="bills20" Grid.Column="2" Grid.Row="3" Margin="7"></TextBox>
Also I have another TextBox where I have done multibinding and done addition of the first three Textboxes like
<TextBox Grid.Column="2" IsReadOnly="True" Grid.Row="7" Grid.ColumnSpan="2" Margin="7" Name="TotalBills">
<TextBox.Text>
<MultiBinding Converter="{ikriv:MathConverter}" ConverterParameter="x+y+z" Mode="TwoWay">
<Binding Path="Text" ElementName="bills100" />
<Binding Path="Text" ElementName="bills50" />
<Binding Path="Text" ElementName="bills20" />
</MultiBinding>
</TextBox.Text>
</TextBox>
I want to bind this multibinding textbox with my class(Transaction) with property as Transaction.Total like my first three textboxes but it shows error
Property text is set more than once
Actually we cannot get the value of a two-way binding from one property and then set the value of another property.
Finally I came with a solution like this
In my Class Transaction
private double _totalBills;
public double TotalBills
{
get { return _totalBills; }
set { _totalBills= value; Notify("TotalBills"); }
}
In XAML(Instead of Multibinding)
<TextBox Text="{Binding Path=Transaction.TotalBills,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Column="2" IsReadOnly="True" Grid.Row="7" Grid.ColumnSpan="2" Margin="7" Name="TotalBills"/>
My ViewModel
public class MainViewModel: INotifyPropertyChanged
{
private Transaction _transactionDetails;
public MainViewModel()
{
Transaction= new Transaction();
_transactionDetails.PropertyChanged += _transactionDetails_PropertyChanged;
}
private void _transactionDetails_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "TotalBills":
_calculate(); //My method for calculation
break;
}
}
}
Related
I am trying to understand how ValueConverters work. I have three Text Boxes being txtQty, txtPrice and txtAmount representing Qty, Price and Amount respectively and Amount = Qty x Price.
txtQty and txtPrice are unbound controls whilst txtAmount is bound to a DataTable in a DataSet.
How can I update the value in txtAmount which is bound to a DataTable using ValueConveter which takes txtQty and txtPrice as input values?
I can easily achieve this in many ways. But I want to use a ValueConverter for this.
Any ideas?
You can create a converter that implements IMultiValueConverter to compute for your Price and Qty.
public class AmountConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
decimal qty = 0;
decimal price = 0;
if (values?.Length < 2)
throw new ArgumentNullException("Parameter should contain 2 values");
if (!string.IsNullOrEmpty(values[0].ToString()) && !decimal.TryParse(values[0].ToString(), out qty))
throw new ArgumentException("1st value should be decimal.");
if (!string.IsNullOrEmpty(values[1].ToString()) && !decimal.TryParse(values[1].ToString(), out price))
throw new ArgumentException("2nd value should be decimal.");
return (qty * price).ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then use MultiBinding for your Amount textbox
<TextBox x:Name="txtAmount" HorizontalAlignment="Left" IsReadOnly="True">
<TextBox.Text>
<MultiBinding Converter="{StaticResource AmountConverter}">
<Binding ElementName="txtQty" Path="Text" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="txtPrice" Path="Text" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
However you may have to do some interactivity with your txtQty and txtPrice to update your viewmodel-bound Amount, you may also need to invoke a command from your vm to accomplish this.
Listing the entire test xaml and viewmodel code...
<Window x:Class="WpfApp2.MainWindow"
x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:interactivity="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
xmlns:vm="clr-namespace:WpfApp2.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:ViewModelTest />
</Window.DataContext>
<Window.Resources>
<local:AmountConverter x:Key="AmountConverter" />
</Window.Resources>
<Grid Margin="12 0 0 0" >
<StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock HorizontalAlignment="Left" Text="Qty" Margin="0 0 12 0" />
<TextBox x:Name="txtQty" HorizontalAlignment="Left" Height="20" Width="50" >
<interactivity:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding DataContext.UpdateAmountCommand, ElementName=root}" CommandParameter="{Binding Path=Text, ElementName=txtAmount}" />
</i:EventTrigger>
</interactivity:Interaction.Triggers>
</TextBox>
</StackPanel>
<StackPanel Orientation="Vertical" >
<TextBlock HorizontalAlignment="Left" Text="Price" Margin="0 0 12 0" />
<TextBox x:Name="txtPrice" HorizontalAlignment="Left" Height="20" Width="50" >
<interactivity:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding DataContext.UpdateAmountCommand, ElementName=root}" CommandParameter="{Binding Path=Text, ElementName=txtAmount}" />
</i:EventTrigger>
</interactivity:Interaction.Triggers>
</TextBox>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock HorizontalAlignment="Left" Text="Amount" Margin="0 0 12 0" />
<TextBox x:Name="txtAmount" HorizontalAlignment="Left" IsReadOnly="True">
<TextBox.Text>
<MultiBinding Converter="{StaticResource AmountConverter}">
<Binding ElementName="txtQty" Path="Text" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="txtPrice" Path="Text" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
</StackPanel>
</Grid>
</Window>
VM
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace WpfApp2.ViewModel
{
public class ViewModelTest : INotifyPropertyChanged
{
public ViewModelTest()
{
UpdateAmountCommand = new CustomCommand<string>(UpdateAmount, (x) => true);
}
private decimal _amount;
public decimal Amount
{
get => _amount;
set
{
if (_amount != value)
{
_amount = value;
OnPropertyChanged();
}
}
}
public CustomCommand<string> UpdateAmountCommand { get; }
private void UpdateAmount(string amountText)
{
Amount = decimal.Parse(amountText);
Debug.WriteLine(Amount);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Not sure if there's an easier way, this is just on top of my head.
PS: You can copy the CustomCommand implementation here.
Hope this helps.
First some background, which can be condensed into editing a property VatCode of VatCodeViewModel.
I have a StockItem with two particular properties in the ViewModel:
public class StockItemViewModel : ViewModelBase
{
private VatCodeViewModel _vatCode;
public VatCodeViewModel VatCode
{
get { return _vatCode; }
set
{
if (_vatCode != value)
{
_vatCode = value;
RaisePropertyChanged("VatCode");
}
}
}
}
The VatCode property accepts a VatCodeViewModel type.
To manage the editing experience, I have a ViewModel called EditStockItemViewModel. This has meta-data such as IsDirty, IsNew, etc., but has the Item property set to the item being edited - in this case an instance of StockItemViewModel. The Item property is in the base class (of TViewModel == StockItemViewModel) ...
public class UnMappedEditableViewModelBase<TViewModel> : ViewModelBase
{
private TViewModel _item;
public TViewModel Item
{
get { return _item; }
set
{
if (_item != value)
{
_item = value;
RaisePropertyChanged("Item");
}
}
}
}
and the implementation class (EditStockItemViewModel, which has Item of StockItemViewModel) ...
public class EditStockItemViewModel : UnMappedEditableViewModelBase<StockItemViewModel>
{
private ObservableCollection<VatCodeViewModel> _vatCodes=new ObservableCollection<VatCodeViewModel>();
public ObservableCollection<VatCodeViewModel> VatCodes
{
get { return _vatCodes; }
set
{
if (_vatCodes != value)
{
_vatCodes = value;
RaisePropertyChanged("VatCodes");
}
}
}
public EditStockItemViewModel()
:base()
{
if (IsInDesignMode)
{
}
else
{
RefreshVatCodesList(null); // refreshes VatCodes property
Save = new RelayCommand(() =>
{
// save functionality snipped
}, () =>
{
bool canExecute = Item.VatCode!=null; // this is ALWAYS null - binding failing
return canExecute;
});
}
}
}
Therefore the ViewModel.Item property is always the item being edited.
A fragment of my view ...
<TextBlock Text="VAT Code:" Grid.Column="1" Grid.Row="3" Style="{StaticResource ComboHeaderTextBlock}" />
<telerik:RadComboBox Grid.Column="2" Grid.Row="3" Style="{StaticResource RadComboBox}" Width="300" HorizontalAlignment="Left"
ItemsSource="{Binding VatCodes}" SelectedValuePath="Item.VatCode"
ClearSelectionButtonVisibility="Collapsed"
CanAutocompleteSelectItems="True"
CanKeyboardNavigationSelectItems="True"
IsEditable="False"
OpenDropDownOnFocus="False"
IsFilteringEnabled="False"
EmptyText="Select ...">
<telerik:RadComboBox.SelectedValue>
<Binding Path="Item.VatCode" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" RelativeSource="{RelativeSource FindAncestor,AncestorType={x:Type UserControl}}" >
<Binding.ValidationRules>
<DataErrorValidationRule />
</Binding.ValidationRules>
</Binding>
</telerik:RadComboBox.SelectedValue>
<telerik:RadComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}" Style="{StaticResource TextBlock}" />
<TextBlock Text="{Binding Name}" Style="{StaticResource DimTextBlock}" />
<TextBlock Text="{Binding ActiveRate.Rate}" Margin="5 5 0 5" />
<TextBlock Text="%" Margin="0 5 5 5" />
</StackPanel>
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
So at the end of all this, I have:
VatCode that binds to [ViewModel].Item.VatCode and uses [ViewModel].VatCodes as source.
The list is populated and appears fine. I know that the ViewModel is binding correctly.
The problem is the VatCode is NOT binding to the Item.VatCode property. So when I get to the Save method, the Item.VatCode property is null (ie. not working).
I am getting the following binding error which appears to be related:
System.Windows.Data Error: 17 : Cannot get 'Item' value (type
'String') from '' (type 'VatCodeViewModel').
BindingExpression:Path=Item.VatCode; DataItem='VatCodeViewModel'
(HashCode=27875274); target element is 'RadComboBox' (Name=''); target
property is 'NoTarget' (type 'Object')
TargetParameterCountException:'System.Reflection.TargetParameterCountException:
Parameter count mismatch.
Clearly the error indicates my binding expression Item.VatCode is suspect, but I'm not sure how to correct it.
I think the problem is in this line of your combo box markup:
ItemsSource="{Binding VatCodes}" SelectedValuePath="Item.VatCode"
This says to pull the items for the combo box from VatCodes (type VatCodeViewModel), and for the combo box value take a property of VatCodeViewModel called Item.VatCode. It's hard to say without seeing the definition of VatCodeViewModel, but I suspect that property doesn't exist.
Here's what I think you're meaning to do. Make SelectedValuePath a property of VatCodeViewModel (let's say VatCode), and then bind the selection to Item.VatCode. You can do it like this:
ItemsSource="{Binding VatCodes}" SelectedValuePath="VatCode" SelectedValue="{Binding Item.VatCode}"
Found it, I was misled by the example provided in the documentation. I should not have used the path SelectedValuePath. So my new code is:
<TextBlock Text="VAT Code:" Grid.Column="1" Grid.Row="3" Style="{StaticResource ComboHeaderTextBlock}" />
<telerik:RadComboBox Grid.Column="2" Grid.Row="3" Style="{StaticResource RadComboBox}" Width="300" HorizontalAlignment="Left"
ItemsSource="{Binding VatCodes}"
ClearSelectionButtonVisibility="Collapsed"
CanAutocompleteSelectItems="True"
CanKeyboardNavigationSelectItems="True"
IsEditable="False"
OpenDropDownOnFocus="False"
IsFilteringEnabled="False"
EmptyText="Select ...">
<telerik:RadComboBox.SelectedValue>
<Binding Path="Item.VatCode" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
<Binding.ValidationRules>
<DataErrorValidationRule />
</Binding.ValidationRules>
</Binding>
</telerik:RadComboBox.SelectedValue>
<telerik:RadComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}" Style="{StaticResource TextBlock}" />
<TextBlock Text="{Binding Name}" Style="{StaticResource DimTextBlock}" />
<TextBlock Text="{Binding ActiveRate.Rate}" Margin="5 5 0 5" />
<TextBlock Text="%" Margin="0 5 5 5" />
</StackPanel>
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
I am working on a WPF and have hit a serious wall. I have a data set that has two columns, ContactName and ContactTitle. I have successfully loaded all of the data into a ComboBox and even sorted it by ContactName. However, I am trying to now access that data and display part of it in a TextBox. (This is of course just a proof of concept type exercise, the final product will populate a variety of TextBoxes with the selected persons information). The problem is, I cannot get the info to populate in the TextBox. Here is the code that I have:
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
namespace MultiBindingInWPF_CS
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
//Create DataSet
CustomersDataSet customerDataSet = new CustomersDataSet();
//Create DataTableAdapter
CustomersDataSetTableAdapters.CustomersTableAdapter taCustomers = new CustomersDataSetTableAdapters.CustomersTableAdapter();
taCustomers.Fill(customerDataSet.Customers);
//Sort Data
SortDescription sd = new SortDescription("ContactName", ListSortDirection.Descending);
//Designate ItemSource
this.ComboBox1.ItemsSource = customerDataSet.Customers;
//Apply Sort
this.ComboBox1.Items.SortDescriptions.Add(sd);
}
private void ComboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
//Using SelectedIndex only to prove connection to TextBox is working
textBox1.Text = ComboBox1.SelectedIndex.ToString();
}
catch
{
textBox1.Text = "Invalid";
}
}
}
}
Then here is my XAML:
<Window x:Class="MultiBindingInWPF_CS.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
Title="Multibinding in WPF" Height="163" Width="300">
<Grid Loaded="Grid_Loaded">
<StackPanel Name="StackPanel1" Margin="12">
<Label Height="28" Name="Label1">List of Customers (Name AND Title :-) )</Label>
<ComboBox Height="23" Name="ComboBox1" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" IsTextSearchEnabled="True" SelectionChanged="ComboBox1_SelectionChanged" SelectedValue="{Binding Path=CustomerID}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} - {1}">
<Binding Path="ContactName" />
<Binding Path="ContactTitle" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Height="23" Name="textBox1" Width="120"/>
</StackPanel>
</Grid>
</Window>
My ultimate goal would be to populate the TextBox dynamically by getting the selected value, and getting the info in the dataset associated with that CustomerID, but just getting the SelectedItem's text to populate in the TextBox would be a huge step.
Any help is GREATLY appreciated. Thanks all.
Give this a try; it removes the changed event handler and leverages binding.
<Grid Loaded="Grid_Loaded">
<StackPanel Name="StackPanel1" Margin="12">
<Label Height="28" Name="Label1">List of Customers (Name AND Title :-) )</Label>
<ComboBox Height="23" Name="ComboBox1" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" IsTextSearchEnabled="True" SelectedValue="{Binding Path=CustomerID}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} - {1}">
<Binding Path="ContactName" />
<Binding Path="ContactTitle" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Height="23" Name="textBox1" Text="{Binding ElementName=ComboBox1, Path=SelectedItem.ContactName}" Width="120"/>
</StackPanel>
</Grid>
Check out this SO answer as well, which details the differences between SelectedItem, SelectedValue, and SelectedValuePath and is ultimately the issue most people run into.
I've got a Treeview with a hierachial template.
Everything works fine. All Objets will respond as expected.
But adding elements to the collection doesn't update the treeview.
My base Object is bind to the treeview.
One of its propertys contains a collection. And this collection has got a property with an own collection.
BaseObject
-> Sub Collection 1
-> SubCollection 2
My BaseObject has implemented INotifyPropertyChanged and my SubCollection 2 has implemented ICollectionChaged.
Nevertheless, wehen I try to add a new Item to SubCollection 2 OnCollectionChaged is called, but CollectionChanged stays null, so nothing happens.
TreeView Templates:
<HierarchicalDataTemplate x:Key="SheetTreeTemplate" >
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="/Resources/Icons/page_green.png" />
<TextBlock FontStyle="Italic">
<TextBlock.Text>
<MultiBinding StringFormat="{}Seite {0}">
<Binding Path="Name"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="DocumentTreeTemplate" ItemsSource="{Binding Path=Sheets.Values}" ItemTemplate="{StaticResource SheetTreeTemplate}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="/Resources/Icons/folder.png" />
<TextBlock FontStyle="Italic">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="DocTypName"/>
<Binding Path="ID"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="PileTreeTemplate" ItemsSource="{Binding Path=Documents.Values}" ItemTemplate="{StaticResource DocumentTreeTemplate}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="/Resources/Icons/report.png" />
<TextBlock FontStyle="Italic" Text="{Binding Path=Name}" />
</StackPanel>
</HierarchicalDataTemplate>
TreeView itself:
<TreeView Style="{DynamicResource NavigationTree}" Name="tvw_mainMenu" ItemsSource="{Binding Values}" ItemTemplate="{DynamicResource PileTreeTemplate}" SelectedItemChanged="tvw_mainMenu_SelectedItemChanged"/>
the Class which should subscribe the SubCollection 2 Changed:
class Sheets : Dictionary<String, Sheet> , INotifyCollectionChanged {
public bool Add(String sKey, Sheet newSheet) {
base.Add(sKey, newSheet);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new KeyValuePair<String, Sheet>(sKey, newSheet)));
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) {
if (CollectionChanged != null) {
CollectionChanged(this, e);
}
}
}
i've found out that there are much more interfaces to implement. Best way is an observable Collection.
Cause I don't want to change all my classes i've found ObservableDictionary
example.
I have below combo box in mvvm-wpf application. I need to implement "Text search" in this..(along with multibinding). Can anybody help me please.
<StackPanel Orientation="Horizontal">
<TextBlock Text="Bid Service Cat ID"
Margin="2"></TextBlock>
<ComboBox Width="200"
Height="20"
SelectedValuePath="BidServiceCategoryId"
SelectedValue="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}},
Path=DataContext.SelectedBidServiceCategoryId.Value}"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}},
Path=DataContext.BenefitCategoryList}"
Margin="12,0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}: {1}">
<Binding Path="BidServiceCategoryId" />
<Binding Path="BidServiceCategoryName" />
</MultiBinding>
</TextBlock.Text></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
Unfortunately, TextSearch.Text doesn't work in a DataTemplate. Otherwise you could have done something like this
<ComboBox ...>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="TextSearch.Text">
<Setter.Value>
<MultiBinding StringFormat="{}{0}: {1}">
<Binding Path="BidServiceCategoryId"/>
<Binding Path="BidServiceCategoryName"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
However this won't work, so I see two solutions to your problem.
First way
You set IsTextSearchEnabled to True for the ComboBox, override ToString in your source class and change the MultiBinding in the TextBlock to a Binding
Xaml
<ComboBox ...
IsTextSearchEnabled="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
Source class
public class TheNameOfYourSourceClass
{
public override string ToString()
{
return String.Format("{0}: {1}", BidServiceCategoryId, BidServiceCategoryName);
}
//...
}
Second Way
If you don't want to override ToString I think you'll have to introduce a new Property in your source class where you combine BidServiceCategoryId and BidServiceCategoryName for the TextSearch.TextPath. In this example I call it BidServiceCategory. For this to work, you'll have to call OnPropertyChanged("BidServiceCategory"); when BidServiceCategoryId or BidServiceCategoryName changes as well. If they are normal CLR properties, you can do this in set, and if they are dependency properties you'll have to use the property changed callback
Xaml
<ComboBox ...
TextSearch.TextPath="BidServiceCategory"
IsTextSearchEnabled="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}: {1}">
<Binding Path="BidServiceCategoryId" />
<Binding Path="BidServiceCategoryName" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
Source class
public class TheNameOfYourSourceClass
{
public string BidServiceCategory
{
get
{
return String.Format("{0}: {1}", BidServiceCategoryId, BidServiceCategoryName);
}
}
private string m_bidServiceCategoryId;
public string BidServiceCategoryId
{
get
{
return m_bidServiceCategoryId;
}
set
{
m_bidServiceCategoryId = value;
OnPropertyChanged("BidServiceCategoryId");
OnPropertyChanged("BidServiceCategory");
}
}
private string m_bidServiceCategoryName;
public string BidServiceCategoryName
{
get
{
return m_bidServiceCategoryName;
}
set
{
m_bidServiceCategoryName = value;
OnPropertyChanged("BidServiceCategoryName");
OnPropertyChanged("BidServiceCategory");
}
}
}
I don't know if your text search has to search ALL the text, but if you want to search from the category ID, you can just set the TextSearch.TextPath property to BidServiceCategoryId. That should also be helpful for anyone who wants to use multibinding and finds that the text search no longer works... It does work if you explicitly set the TextPath property.