WPF/C# Label Content to ComboBox - c#

I have a WPF/C# app with a tiny issue. I have a label that is passing the string value of AZ. I have a combobox that has AX, AY, and AZ values bound to the database. For some reason, the label value is not matching to the combobox value.
So, I have
CbCode.SelectedValue = Code;
Code is coming from another window
public SystemCode(string Code)
{
InitializeComponent();
Code = sysCode;
}
public string Code { get; }
The debugger tells me the string value is AZ, bu the combobox in the screen is the default the value of AX.
So how do I get the label content to set the page load value of the combobox?
XAML:
<ComboBox x:Name="CbCode" DataContext="{StaticResource CodeViewSource}"
SelectedValuePath="Code" ItemsSource="{Binding}" DisplayMemberPath="Code"
HorizontalAlignment="Left" Margin="106,103,0,116.4" Width="97"
d:LayoutOverrides="Height"/> <TextBox x:Name="TxtZipFive"
HorizontalAlignment="Left" Height="23" Margin="106,0,0,85.4"
TextWrapping="Wrap" Text="" VerticalAlignment="Bottom" Width="97"/>

Your question is not clear enough, but I think you need to use:
UpdateSourceTrigger=PropertyChanged

Related

Change field of a TextBox with Button click at the same index in Data Binded List View

I have a ListView with a collection as ItemsSource.
<ListView x:Name="lvBT" Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}"
ItemsSource="{x:Bind ViewModel.CurrentPoste.TableauxBT}" Margin="0,0,0,12"
IsEnabled="{x:Bind ViewModel.CurrentPoste.BtEdition, Mode=TwoWay}"
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:BT">
[...]
<TextBox x:Name="tbNumSerieBT" HorizontalAlignment="Stretch" Margin="12,32,16,0" Text="{x:Bind NumSerie, Mode=TwoWay}" VerticalAlignment="Top" Grid.Column="3" FontSize="16" Grid.ColumnSpan="2"/>
<Button x:Name="bScannerBT" Grid.Column="5" HorizontalAlignment="Stretch" Margin="12,32,15,0" VerticalAlignment="Top" Content="Scanner tabeau BT" FontSize="14" Click="BScannerBT_Click"/>
[...]
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
tableauxBT is representing a collection of BT objects and for each object in that collection I'm creating a DataTemplate, like basic data binding.
When I click on the Button in that template I'm scanning a barcode with a BarcodeScanner and want to put the return value in the TextBox field.
For each button of each different BT item I want to scan a different barcode but the problem is that I don't know how to get the index of the Button that was clicked to put the value in the right TextBox.
So how can I do to get the index of the clicked Button to put the value in the TextBox at the same index ?
You can get the index of the clicked button using the "BScannerBT_Click" method.
public BScannerBT_Click(object sender, EventArgs e)
{
var myClickedButton = (Button)sender; //this object hold all information you need.
//You can reach the button's dataContext and change the value you want to.
var buttonDataContext = myClickedButton.DataContext.
//now you have the ViewModel (buttonDataContext) associated with the 'NumSerie'
}
I hope it helps you.

Getting the text value selected in at ListBox in C#

I have a list box
<Label Content="Report" HorizontalAlignment="Left" Height="47" Margin="36,75,0,0" VerticalAlignment="Top" Width="63"/>
<ListBox x:Name="ListBox1" HorizontalAlignment="Left" Height="121" Margin="84,75,0,0" VerticalAlignment="Top" Width="102" SelectionChanged="ListBox_SelectionChanged" SelectedIndex="-1">
<ListBox Height="100" Width="100" SelectionChanged="ListBox_SelectionChanged_1">
<ListBoxItem x:Name="ListBoxFAT" Content="FAT"/>
<ListBoxItem x:Name="ListBoxNUMI" Content="NUMI"/>
<ListBoxItem x:Name="ListBoxSSID" Content="SSID"/>
<ListBoxItem x:Name="ListBoxFact" Content="FACT"/>
</ListBox>
</ListBox>
This was created by dragging the listbox icon from the tool bar. I added items and their values.
Now I am trying to just get the text value of the selected item.
private void ListBox_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
string text = (string)ListBox1.SelectedValue;
MessageBox.Show(text);
I have also tried SelectedItem
string text = (string)ListBox1.SelectedItem;
But the message box is always blank.
This should be simple, but I have been working on it for hours, and trying every suggestion or answer on stackoverflow. Most suggestions do not even compile. For example:
string selected = listBox1.GetItemText(listBox1.SelectedValue);
Will not compile. GetItemText is not found. I am using Visual Studio 17. "'ListBox does not contain a definition for 'GetItemText'..."
Any thoughts? Please advise. Thanks.
Thanks for the comment, Charles. I did that.
Playing further, now I get
System.InvalidCastException: 'Unable to cast object of type 'System.Windows.Controls.ListBoxItem' to type 'System.String'.'
string text = (string)ListBox1.SelectedItem;
As indicated by Charles May, your XAML shows that your ListBox is within another ListBox, which is why you're getting errors being raised..
The event being called "ListBox_SelectionChanged_1" is bound to the ListBox object inside ListBox1, which is unnamed.
I believe that the behaviour you are looking for would be fixed like this:
XAML:
<Label Content="Report" HorizontalAlignment="Left" Height="47" Margin="36,75,0,0" VerticalAlignment="Top" Width="63"/>
<ListBox x:Name="ListBox1" HorizontalAlignment="Left" Height="121" Margin="84,75,0,0" VerticalAlignment="Top" Width="102" SelectionChanged="ListBox_SelectionChanged" SelectedIndex="-1">
<ListBoxItem x:Name="ListBoxFAT" Content="FAT"/>
<ListBoxItem x:Name="ListBoxNUMI" Content="NUMI"/>
<ListBoxItem x:Name="ListBoxSSID" Content="SSID"/>
<ListBoxItem x:Name="ListBoxFact" Content="FACT"/>
</ListBox>
Code Behind:
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string text = ((sender as ListBox)?.SelectedItem as ListBoxItem)?.Content.ToString();
MessageBox.Show(text);
}
Or at least something close to this solution.
In the markup, SelectedIndex is set to -1 which means there is no selection. In this case, SelectedValue and SelectedItem both return null. You can solve this either by setting SelectedIndex to a value between 0 and 3 or by preparing your code to cope with a null value in SelectedValue and SelectedItem, e.g.
string text = (ListBox1.SelectedItem as ListBoxItem)?.Content?.ToString();
This won't raise an error so that the user can select an item afterwards. With a selection, the text should be displayed as expected.

Why does my dropdown feel so clunky?

I have a XAML UserControl embedded in a WinForms/WPF Interop ElementHost control. The control is pretty simple - it's just a dropdown with a button - here's the entire markup:
<UserControl x:Class="Rubberduck.UI.FindSymbol.FindSymbolControl"
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:Rubberduck.UI.FindSymbol"
mc:Ignorable="d"
d:DesignHeight="27" d:DesignWidth="270">
<UserControl.Resources>
<local:DeclarationImageConverter x:Key="DeclarationImageConverter" />
</UserControl.Resources>
<UserControl.CommandBindings>
<CommandBinding Command="local:FindSymbolControl.GoCommand"
Executed="CommandBinding_OnExecuted"
CanExecute="CommandBinding_OnCanExecute"/>
</UserControl.CommandBindings>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<ComboBox IsEditable="True"
ItemsSource="{Binding MatchResults}"
SelectedItem="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}"
IsTextSearchCaseSensitive="False"
IsTextSearchEnabled="True"
TextSearch.TextPath="IdentifierName">
<ComboBox.ItemTemplate>
<DataTemplate DataType="local:SearchResult">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Image Height="16" Width="16" Margin="2,0,2,0" Source="{Binding Declaration, Converter={StaticResource DeclarationImageConverter}}" />
<TextBlock Margin="2,0,2,0" Text="{Binding IdentifierName}" FontWeight="Bold" MinWidth="140" />
<TextBlock Margin="2,0,2,0" Text="{Binding Location}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Grid.Column="1"
Command="local:FindSymbolControl.GoCommand">
<Image Height="16" Source="pack://application:,,,/Rubberduck;component/Resources/arrow.png" />
</Button>
</Grid>
</UserControl>
The problem is that it doesn't work reliably, and far from instinctively.
If I type something in the box that actually matches an item, nothing happens until I manually select that item in the dropdown. Like here, I typed "sleepD", the box autocompleted to "sleepDelay", but the command is still disabled:
Once I've selected the item in the dropdown, the command button gets enabled as expected (although the image on the button doesn't show up grayed-out when the button is disabled, so it's not exactly as obvious as I intended it to be).
(the screenshot isn't really showing it, but there's only 1 match for that search)
If I click the button at that point, it works as expected. The problem is that if I make a new selection from the dropdown after that, the text box gets cleared instead of displaying the item I selected, and there's a weird delay during which the box is displaying what appears to be selected whitespace - this only seems to happen when the previous selection was made after selecting a value in the dropdown while the search text matches multiple entries, like "Sleep" above.
After the box got cleared, I can make a new selection from the dropdown and it will work as expected (except the VBE won't actually activate the CodePane I'm setting the selection to, but that's a separate issue).
The command implementation simply raises a Navigate event that passes a Declaration to the code that owns the VM instance.
The Search method, for which I need to add a .Take(50) after the .Select, to limit the number of returned results and perhaps reduce the lag a bit:
private void Search(string value)
{
var lower = value.ToLowerInvariant();
var results = _declarations.Where(
declaration => declaration.IdentifierName.ToLowerInvariant().Contains(lower))
.OrderBy(declaration => declaration.IdentifierName.ToLowerInvariant())
.Select(declaration => new SearchResult(declaration));
MatchResults = new ObservableCollection<SearchResult>(results);
}
private string _searchString;
public string SearchString
{
get { return _searchString; }
set
{
_searchString = value;
Search(value);
}
}
private SearchResult _selectedItem;
public SearchResult SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
}
}
private ObservableCollection<SearchResult> _matchResults;
public ObservableCollection<SearchResult> MatchResults
{
get { return _matchResults; }
set { _matchResults = value; OnPropertyChanged(); }
}
}
There's also an IValueConverter involved, that takes the Declaration in the SearchResult and switches on the declaration's DeclarationType enum to return a pack uri that points to the .png image to use in the dropdown list.
Aaah found it. It was all in the XAML.
Right here:
Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}"
That line doesn't belong there; binding the TextSearch.Text property instead...
TextSearch.Text="{Binding SearchString, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
Makes it all work as intended. No glitch, no lag. Well there is a lag when I first drop the dropdown, but that's another issue.
Lesson learned: when TextSearch is enabled on an editable combobox, don't bind the Text property, unless you want weird behavior.

Setter not getting called for TextBox Text property in Data Model

In one of my Data Models I have a string property that is bound to the text of a textBox. When I change the text of this textBox I would like to update a property in another ViewModel. To achieve this, I believe that I would have to perform the operations under the property's setter, but whenever I change the textbox's text the only area of the string property that is called is the getter. There must be something that I don't know or am overlooking..
This is the string property that I am working with just in case it helps to see it:
public string DisplayName
{
get { return _displayName; }
set
{
_displayName = value;
NotifyPropertyChange(() => DisplayName);
//These values are from the other ViewModel
if (MainTreeCollection.SelectedItem != null
&& value != MainTreeCollection.SelectedItem.DisplayName)
MainTreeCollection.SelectedItem.DisplayName = value;
}
}
I noticed in this question he shows how to "get [his] ViewModel to know when the user has changed text in the TextBox and moved the focus away from the TextBox". It looks like he has achieved this by binding to a string property, which is what I did above.
How come the property's setter is not getting accessed? How would I change my method and code to perform these operations?
Update: This is the xaml of the textBox as requested:
<TextBox Text="{Binding Model.DisplayName}" Height="23"
HorizontalAlignment="Left" Name="title_TB"
VerticalAlignment="Top" Width="Auto" FontWeight="Bold"
FontSize="14" Margin="5,2,0,0" />
In case you want bounded property to be set while you are typing into it, set UpdateSourceTrigger to PropertyChanged.
Default value is LostFocus for TextBox i.e. bounded property gets updated only when LostFocus event gets raised for TextBox.
<TextBox Text="{Binding Model.DisplayName,
UpdateSourceTrigger=PropertyChanged}"
Height="23" HorizontalAlignment="Left" Name="title_TB"
VerticalAlignment="Top" Width="Auto" FontWeight="Bold"
FontSize="14" Margin="5,2,0,0"/>

WPF, property bound to dependent ComboBox always gives initial value

I have a ComboBox that needs to depend on the value of another ComboBox. This part already works, with the dependent ComboBox refreshing when a new value is chosen in the independent ComboBox:
<!-- Independent -->
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="2"
x:Name="cbo_product" VerticalAlignment="Center" Width="120"
ItemsSource="{Binding Source={StaticResource productsXml}}"
DisplayMemberPath="#name" SelectedValuePath="#name"
SelectionChanged="cbo_product_SelectionChanged"
SelectedValue="{Binding Path=Product}" />
<!-- Dependent -->
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="2"
x:Name="cbo_component" VerticalAlignment="Center" Width="201"
DataContext="{Binding SelectedItem, ElementName=cbo_product}"
ItemsSource="{Binding XPath=Components/Component}"
DisplayMemberPath="#name" SelectedValuePath="#name"
SelectedValue="{Binding Path=Component}"
SelectionChanged="cbo_component_SelectionChanged" />
In the C# class behind this, I have:
public MyUserControlConstructor()
{
MyViewModelInstance= new MyViewModel();
DataContext = MyViewModelInstance;
}
And in MyViewModel, I have:
public string Component
{
get { return _component; }
set
{
if (value == _component)
{
return;
}
_component = value;
onPropertyChanged(PropertyNames.Component);
}
}
private void onPropertyChanged(PropertyNames fieldName)
{
if (null == PropertyChanged)
{
return;
}
string propertyName = Enum.GetName(typeof(PropertyNames), fieldName);
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
When I change the dependent ComboBox (Component), it shows up with the new value in my app, of course. However, when I hit a button that causes the value of the Component property to be displayed, it is always the initial value, and not the value I just chose in the ComboBox. I think there must be an error in my XAML. For the C#, I tried to follow a combination of this and this guide. How do I tie my dependent ComboBox to XML values nested in the SelectedItem of the independent ComboBox, but still update the Component property in my class?
Edit: my suspicion is that things are wonky because I set the DataContext for the dependent ComboBox in two places: first in the constructor in C#, to my view model, and second in the XAML, to DataContext="{Binding SelectedItem, ElementName=cbo_product}".
Edit: I had been setting initial values in the constructor to my view model class. When I take out the initial value for the Component property, then even after I change the selected value in the dependent ComboBox, I still get no value from the Component property. This pretty much just rehashes what I already knew: the dependent ComboBox is tied to the independent ComboBox (it gets its data from the independent ComboBox, that is), but not to the Component property.
Edit: by request, here's a sample of my XML:
<Products xmlns="">
<Product name="Awesomeness">
<Components>
<Component name="Component of Glory"/>
<Component name="Component of Doom"/>
</Components>
</Product>
</Products>
Edit: I'm guessing a MultiBinding would be of use, after looking at this and this.
Edit: it seems like I should be able to get the dependent ComboBox to work without setting DataContext, just by using ItemsSource:
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="2"
x:Name="cbo_component" VerticalAlignment="Center" Width="201"
ItemsSource="{Binding ElementName=cbo_product, Path=SelectedItem,
XPath=Components/Component}"
DisplayMemberPath="#name" SelectedValuePath="#name"
SelectedValue="{Binding Path=Component}"
SelectionChanged="cbo_component_SelectionChanged"/>
However, this doesn't work: the dependent ComboBox is empty, instead of showing all the Component names.
The way I found of getting around this involves setting the ItemsSource in C# instead of XAML, which I would prefer not to do. However, it works, and after a day and a half of banging on this, it's the best I came up with.
In XAML:
<!-- Independent -->
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="2"
x:Name="cbo_product" VerticalAlignment="Center" Width="120"
ItemsSource="{Binding Source={StaticResource productsXml}}"
DisplayMemberPath="#name" SelectedValuePath="#name"
SelectionChanged="cbo_product_SelectionChanged"
SelectedItem="{Binding Path=ProductNode}"
SelectedValue="{Binding Path=Product}" />
<!-- Dependent -->
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="2"
x:Name="cbo_component" VerticalAlignment="Center" Width="201"
DisplayMemberPath="#name" SelectedValuePath="#name"
SelectedValue="{Binding Path=Component, Mode=TwoWay}"
SelectionChanged="cbo_component_SelectionChanged"/>
In C#, the event handler for when the independent ComboBox changes:
private void cbo_product_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
// Set ItemsSource of dependent ComboBox
cbo_component.ItemsSource = getChildNodesFromComboBox(
sender as ComboBox, "Components/Component"
);
cbo_component.SelectedIndex = 0;
}
// Helper method to do XPath query and get child nodes from selected value of
// independent ComboBox
private XmlNodeList getChildNodesFromComboBox(ComboBox comboBox,
string xpath)
{
if (null == comboBox)
{
return null;
}
var xml = comboBox.SelectedItem as XmlElement;
if (null == xml)
{
return null;
}
return xml.SelectNodes(xpath);
}
Now the Component property in my view model class, to which my dependent ComboBox is bound in XAML, gets populated with the value selected in the dependent ComboBox because I didn't have to change the DataContext of the dependent ComboBox.

Categories

Resources