I have a not editable ComboBox to display all tables of and SQL Database.
<ComboBox Grid.Column="1"
Grid.Row="2"
Height="23"
Margin="3,3,3,3" Name="cbLogTable" VerticalAlignment="Top"
ItemsSource="{Binding}"
TextSearch.TextPath="TABLE_NAME"
SelectedValue="{Binding Path=LogTable, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, ValidatesOnDataErrors=True}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TABLE_NAME}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The property of the containing UserControl looks like this and also implements the INotifyPropertyChanged:
public string LogTable
{
get
{
return _logTable;
}
set
{
if (_logTable == value) return;
_logTable = value;
OnPropertyChanged("LogTable");
}
}
I use the following data binding to fill the ComboBox:
private void UpdateLogTable()
{
var connection = new SqlConnection(_connectionString);
connection.Open();
DataTable t = connection.GetSchema("Tables");
cbLogTable.DataContext = t;
connection.Close();
}
But I don't receive a PropertyChanged notification on changing the selected value of the ComboBox. Where is my fault?
In the binding of SelectedValue:
SelectedValue="{Binding Path=LogTable,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay,
ValidatesOnDataErrors=True,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}}}"
Otherwise, the binding is looking for LogTable property on the DataTable type (which is the DataContext for the Combobox), and fails silently.
Related
I'm trying to create an edit form for editing properties of a custom set of TV Series objects. One of the properties holds a collection of all owned media formats (DVD, Blu-ray, etc) for that particular series that will be displayed in a ComboBox. Items are added to the ComboBox via a separate popup window and items are to be removed from the ComboBox by selecting the item and clicking a remove Button.
I can add new entries to the MediaOwned ComboBox just fine, but when I try to select a specific ComboBox item to test the remove Button I find that I can only ever select the first entry. Can someone please tell me if I've missed something embarrassingly obvious, thanks.
Here is the problematic property:
private ObservableCollection<string> _mediaOwned = new ObservableCollection<string>();
public ObservableCollection<string> MediaOwned
{
get { return _mediaOwned; }
set
{
_mediaOwned = value;
OnPropertyChanged(new PropertyChangedEventArgs("MediaOwned"));
}
}
Here are the other relevant code behind:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Create binding for the ListBox.
Binding listBinding = new Binding();
listBinding.Source = show.Series;
listBinding.Mode = BindingMode.OneWay;
listBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
lbSeries.SetBinding(ListBox.ItemsSourceProperty, listBinding);
// Create binding for the ComboBox.
Binding myBinding = new Binding();
myBinding.Path = new PropertyPath("MediaOwned");
myBinding.Mode = BindingMode.TwoWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
cbMediaOwned.SetBinding(ComboBox.ItemsSourceProperty, myBinding);
}
private void btnRemoveMedia_Click(object sender, RoutedEventArgs e)
{
Series series = (Series)lbSeries.SelectedItem;
series.MediaOwned.Remove(cbMediaOwned.Text);
}
And here is the XAML code:
<Border Style="{StaticResource PanelBorderStyle}" DockPanel.Dock="Left" Margin="0,8,8,0"
DataContext="{Binding ElementName=lbLists, Path=SelectedItem}">
<DockPanel VerticalAlignment="Top">
<StackPanel>
<ListBox x:Name="lbSeries" Style="{StaticResource BasicListStyle}" Width="180" Height="300"
DisplayMemberPath="Title" SelectionMode="Single" LayoutUpdated="lbSeries_LayoutUpdated">
</ListBox>
</StackPanel>
<StackPanel x:Name="editPanel" DataContext="{Binding ElementName=lbSeries, Path=SelectedItem}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 4, 0, 0">
<TextBlock Style="{StaticResource SmallFont}" Width="100">Title</TextBlock>
<TextBox x:Name="txtTitle" Style="{StaticResource TextBoxStyle}" Text="{Binding Path=Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="200" Margin="8, 8, 16, 8"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBlock Style="{StaticResource SmallFont}" Width="100">Media owned</TextBlock>
<ComboBox x:Name="cbMediaOwned" Style="{StaticResource ComboBoxStyle}" Width="150" Margin="8,8,6,8"
></ComboBox>
<Button x:Name="btnAddMedia" Style="{StaticResource ToolbarButtonStyle}" Click="btnAddMedia_Click" Margin="0">
<StackPanel ToolTip="Add media">
<Image Source="Images/add.png" />
</StackPanel>
</Button>
<Button x:Name="btnRemoveMedia" Style="{StaticResource ToolbarButtonStyle}" Click="btnRemoveMedia_Click" Margin="4">
<StackPanel ToolTip="Remove media">
<Image Source="Images/remove.png" />
</StackPanel>
</Button>
</StackPanel>
</StackPanel>
</DockPanel>
</Border>
Alternatively I can also remove the binding code in the code behind and replace the ComboBox with the below code (but I still get the same problem - I can't select anything in the ComboBox):
<ComboBox x:Name="cbMediaOwned" Style="{StaticResource ComboBoxStyle}" Width="150" Margin="8,8,6,8" ItemsSource="{Binding ElementName=lbSeries, Path=SelectedItem.MediaOwned, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedMedia, UpdateSourceTrigger=PropertyChanged}"></ComboBox>
SelectedMedia property:
private string _selectedMedia = "";
public string SelectedMedia
{
get { return _selectedMedia; }
set
{
_selectedMedia = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedMedia"));
}
}
Here is my xaml:
<ComboBox x:Name="Models_ComboBox"
Width="110"
Text="Model"
ItemsSource="{Binding Models}"
SelectedItem="{Binding SelectedModel}"
DisplayMemberPath="Model"
MouseDoubleClick="Models_ComboBox_MouseDoubleClick"
SelectionChanged="Models_ComboBox_SelectionChanged"/>
Here are my VM properties:
private DataTable models;
public DataTable Models
{
get { return models; }
set
{
if (models != value)
{
models = value;
OnPropertyChanged(nameof(Models));
}
}
}
and
private DataRowView selectedModel;
public DataRowView SelectedModel
{
get { return selectedModel; }
set
{
if (selectedModel != value)
{
selectedModel = value;
if (value != null)
{
InitializeOptions(value["Model"].ToString());
}
OnPropertyChanged(nameof(SelectedModel));
}
}
}
As you can see, the ItemsSource and the SelectedItem of the ComboBox are bound to two different properties in the ViewModel. The ItemsSource is bound to a DataTable populated from a Database. Once the user selects a Model, then there are other option ComboBoxes that are populated based on that selection.
Fixed the problem myself. I had a line of code that was automatically setting the SelectedIndex of the ComboBox without me realizing.
I have editable combobox in WPF. which is having list of order number.
I need to implement below scenario in my code.
The user can fill the starting of order number, and, the system propose the close order number available in the dropdown list.
Can anyone suggest how to do that?
In My Viewmodel i have written:
public void _fillREOrderNumbers()
{
List<FinishedReprintingOrderNumber> orders = _finishedProductReprintService.GetFinishedProductReprintbyOrder().ToList();
foreach (var item in orders)
{
ReOrders.Add(item);
}
}
This is loading the order number in drop down.
View or XAML:
<ComboBox x:Name="cbOFab" HorizontalAlignment="Left" Margin="373,81,0,0"
VerticalAlignment="Top" Width="262" IsEditable="True"
ItemsSource="{Binding ReOrders, Mode=TwoWay}" DisplayMemberPath="codOrder" SelectedItem="{Binding
ReSelectedOrder}" Background="{DynamicResource dgridRowColor}" />
Till Now,
I am able to populate the order number in my combo box but I am not aware how to search inside it.
I have implemented filtering of items in ComboBox this way.
Here is XAML:
<ComboBox
MinWidth="200"
ItemsSource="{Binding Path=Shops.View, RelativeSource={RelativeSource TemplatedParent}}"
DisplayMemberPath="NameExtended"
SelectedItem="{Binding Path=SelectedShop, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsTextSearchEnabled="False"
IsEditable="True"
IsDropDownOpen="{Binding Path=ComboOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
StaysOpenOnEdit="True"
Text="{Binding Path=SearchText, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction
Command="{Binding Path=FilterCommand, RelativeSource={RelativeSource TemplatedParent}}"
/>
</i:EventTrigger>
</i:Interaction.Triggers>
You need all lines from IsTextSearchEnabled and below.
When you press any key in combo box, it opens in up and filters items in it, using property SearchText bound to ComboBox.Text.
Here is the view model code:
public string SearchText { get; set; }
private List<Shop> _shops;
protected void FilterShops()
{
ComboOpen = true;
if (!string.IsNullOrEmpty(SearchText))
{
Shops.UpdateSource(_shops.Where(s => s.NameExtended.ToLower().Contains(SearchText.ToLower())));
}
else
{
Shops.UpdateSource(_shops);
}
OnPropertyChanged("Shops");
}
I've created a FilterCombobox that inherits from Combobox.
This FilterCombobox filters the result based on the users input.
In my view I have two of these FilterCombobox's and they reference the same ObserveableCollection.
I can filter in one FilterCombobox1 without filter the other, but
When I change focus to the next FilterCombobox2, the FilterCombobox1 is filtered according to the filter applied in FilterCombobx2 after the third'ish letter (looks like this a recurring pattern)
If I have two seperate ObserveableCollection then this issue does not invoke, so I mistake that my filter method does something to the actual ObserveableCollection.
Filter method:
private void SetFilterToTextInput()
{
Items.Filter += a =>
{
var propertyString = GetPropertyString(a);
if (propertyString.ToUpper().Contains(Text.ToUpper()))
return true;
return false;
};
}
This is my XAML implementation of these two FilterComboboxes:
<Controller:FilterComboBox
ItemsSource="{Binding Path=Collection,UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValue="{Binding Left, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Width="135"
/>
<TextBlock Margin="0,5,5,0" FontSize="16" VerticalAlignment="Center" >-</TextBlock>
<Controller:FilterComboBox
ItemsSource="{Binding Path=Collection, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValue="{Binding Right, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Width="135"
/>
I have a Window with ListBox on the left Side and TextBox on the right Side. The Textbox is binding to the Selected Item of the ListBox. The Textbox has a SaveContentCommand.
If you leave the Textbox with Enter or the Tab the SaveContentCommand is executed correct. But if i use the mouse to select something else the selecteditem is changed and then the SaveContentCommand is executed. This means the SaveContentCommand is used on another item.
I have tried to hack something like RenameLastSelectedItem()
But is there a correct/better way?
My List:
<customControls:MyListBox x:Name="UserListBox"
Grid.Row="1"
Grid.Column="0"
ItemsSource="{Binding Users}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top"
Style="{DynamicResource MyListBoxStyle}"
ItemContainerStyle="{DynamicResource MyListBoxItemUserListStyle}">
My TextBox:
<customControls:MyTextBox x:Uid="textBoxName"
x:Name="textBoxNameOfSelectedItems"
Text="{Binding SelectedItem.Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true, Mode=TwoWay}"
focus:FocusExtension.IsFocused="{Binding SelectedItem.IsNameFocused, Mode=TwoWay}"
focus:FocusExtension.EnableSelection="True"
UseKeyboardBinding="true"
Style="{DynamicResource MyTextBoxStyle}"
SaveContentCommand="{Binding SelectedItem.UpdateCommand}"
In the set method of the SelectedItem, save the previous item. This code will run when the selected item is changed when selecting a new item with the mouse..
SelectedItem
{get { return _selectedItem;}
set
{
//null check
SaveContent(_selectedItem);
_selectedItem = value;
}
}
I am trying to bind an ItemsSource to a ComboBox in a RowDetailsTemplate. If I place a ComboBox outside the grid it works fine. I think this is occureing because of the ItemsSource property on the grid may be throwing off the ComboBox within the RowDetailsTemplate. XAML is below any thoughts?
Categories and CatTypes are two different ObservableCollections.
No error is occurring; the ComboBox just appears empty.
<ComboBox ItemsSource="{Binding CatTypes}"></ComboBox>
<my:DataGrid Name="gridProds" AutoGenerateColumns="False"
AlternatingRowBackground="Gainsboro" ItemsSource="{Binding Categories}">
<my:DataGrid.Columns>
<my:DataGridTextColumn x:Name="CatId" Header="CatID" Width="Auto" Binding="{Binding CategoryID}" />
<my:DataGridTextColumn Header="CatName" Width="Auto" Binding="{Binding CategoryName}" />
</my:DataGrid.Columns>
<my:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label>ID:</Label>
<TextBox Name="txtGridCatId" Text="{Binding CategoryID}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Category Type:</Label>
<ComboBox ItemsSource="{Binding CatTypes}"></ComboBox>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</my:DataGrid.RowDetailsTemplate>
</my:DataGrid>
There is a class in the called DataSource in which the following is done:
private ObservableCollection<string> _cattypes = new ObservableCollection<string> { };
public ObservableCollection<string> CatTypes
{
get
{
_cattypes = new ObservableCollection<string> { };
SqlConnection con = new SqlConnection("MyConnStringHere;");
SqlCommand cmd = new SqlCommand("Select ID, CatType from PfCategoryType ORDER BY CatType", con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
string CatType = (string)rdr["CatType"];
_cattypes.Add(CatType);
}
con.Close();
return _cattypes;
}
}
In the MainWindow.xaml.cs I have:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataSource dataSource = new DataSource();
this.DataContext = dataSource;
}
}
If you checked the debug output in VS you would see the actual binding error. Most likely below code will fix it for you.
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=CatTypes}" />
If you can't get RelativeSource to work then use names. The property CatTypes is a property of some class which you created an object for and set as datacontext to some control. Just give that control a name (for example myControl) and bind like this:
<ComboBox ItemsSource="{Binding ElementName=myControl, Path=CatTypes}" />
If that don't work you need to post more of your code to figure out what you are doing wrong.
What happens if you try this?
<ComboBox DataContext="{Binding DataContext, ElementName=myControl}" ItemsSource="{Binding CatTypes}" />
(Of course you'd rename "myControl" to match the name of your window.)
Here, we're setting the data context of the combo box to be the same as the data context of the window. Since this is also the same data context of the first combo box in your XAML, I imagine the second combo box will start behaving like the first. (Although I worry that this will result in some unnecessary database connections, one per grid row.)
On second thought, if you need to set other properties in the context of the row, you won't want to set the data context of the entire ComboBox. In that case, I'd try something like this.
<ComboBox ItemsSource="{Binding ElementName=myControl, Path=DataContext.CatTypes}" SelectedItem="{Binding CategoryType}" />