I'm using the MVVM pattern, and I've created a binding in XAML for the SelectedItem of a DataGrid. I programatically set the SelectedItem, however when I do so the DataGrid does not scroll to the selection. Is there any way I can achieve this without completely breaking the MVVM pattern?
I found the following solution but I get an error when I try to implement the Behavior class, even though I've installed Blend SDK: http://www.codeproject.com/Tips/125583/ScrollIntoView-for-a-DataGrid-when-using-MVVM
This should work. The idea is you have this attached property that you will attach to the DataGrid. In the xaml where you attach it, you'll bind it to a property on your ViewModel. Whenever you want to programmatically assign a value to the SelectedItem, you also set a value to this property, which the attached property is bound to.
I've made the attached property type to be whatever the SelectedItem type is, but honestly it doesn't matter what the type is as long as you set it to something different than what it was before. This attached property is just being used as a means to execute some code on the view control (in this case, a DataGrid) in an MVVM friendly fashion.
So, that said, here's the code for the attached property:
namespace MyAttachedProperties
{
public class SelectingItemAttachedProperty
{
public static readonly DependencyProperty SelectingItemProperty = DependencyProperty.RegisterAttached(
"SelectingItem",
typeof(MySelectionType),
typeof(SelectingItemAttachedProperty),
new PropertyMetadata(default(MySelectionType), OnSelectingItemChanged));
public static MySelectionType GetSelectingItem(DependencyObject target)
{
return (MySelectionType)target.GetValue(SelectingItemProperty);
}
public static void SetSelectingItem(DependencyObject target, MySelectionType value)
{
target.SetValue(SelectingItemProperty, value);
}
static void OnSelectingItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var grid = sender as DataGrid;
if (grid == null || grid.SelectedItem == null)
return;
// Works with .Net 4.5
grid.Dispatcher.InvokeAsync(() =>
{
grid.UpdateLayout();
grid.ScrollIntoView(grid.SelectedItem, null);
});
// Works with .Net 4.0
grid.Dispatcher.BeginInvoke((Action)(() =>
{
grid.UpdateLayout();
grid.ScrollIntoView(grid.SelectedItem, null);
}));
}
}
}
And here's the xaml snippet:
<Window ...
xmlns:attachedProperties="clr-namespace:MyAttachedProperties">
...
<DataGrid
attachedProperties:SelectingItemAttachedProperty.SelectingItem="{Binding MyViewModel.SelectingItem}">
...
</DataGrid>
</Grid>
I am new to MVVM. I understand the idea of MVVM and try to implement everything correctly.
I had a similar problem to above and I ended up with 1 line in XAML and 1 line in code behind. The rest of the code is in the VM.
I did the following in XAML
<ListBox DockPanel.Dock="Top"
Name="Selection1List"
ItemsSource="{Binding SelectedList1ItemsSource}"
SelectedItem="{Binding SelectedList1Item}"
SelectedIndex="{Binding SelectedList1SelectedIndex}"
SelectionChanged="Selection1List_SelectionChanged">
And this in the code behind:
private void Selection1List_SelectionChanged(object sender, SelectionChangedEventArgs e) {
Selection1List.ScrollIntoView(Selection1List.SelectedItem);
}
and this works fine.
I know some people don't want even one line of code in the code behind the window. But I think this 1 line is just for the view. It has nothing to do with the data or with the logic of the data. So I would think this is no violation of the MVVM principle - and so much easier to implement.
Any comments are welcome.
The solution of #Edgar works fine, but in my application I had to check the OriginalSource of the SelectionChangedEventArgs as well.
private void OperatorQualificationsTable_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if ((OperatorQualificationsTable.SelectedItem != null) && (e.OriginalSource?.Equals(OperatorQualificationsTable) ?? false))
{
OperatorQualificationsTable.ScrollIntoView(OperatorQualificationsTable.SelectedItem);
}
}
My datagrid contains following ComboBoxColumn
<dgx:EnhancedDataGridComboBoxColumn
DisplayMemberPath="DescriptionNL"
Header="{x:Static nl:Strings.Label_Qualification}"
ItemsSource="{Binding Path=QualificationKeysView, Source={StaticResource ViewModel}}"
SelectedValueBinding="{Binding ActivityQualification.QualificationKey}"
SelectedValuePath="QualificationKey"/>
Everytime when I scrolled up or down the selction changed event was called for the Combobox and it was no longer possible to move the selected item out of the view.
This is my solution to get ScrollIntoView working. I perform the operation in the LayoutUpdated() event
public void ManipulateData()
{
// Add a new record or what else is needed;
myItemsSourceCollection.Add(...);
// Not needed when the ItemsSource is a ObervableCollectin
// with correct Binding (ItemsSource="{ Binding myItemsSourceElement }")
myDataGrid.Items.Refresh();
// Goto last Item or where ever
myDataGrid.SelectedIndex = this.myDataGrid.Items.Count - 1;
}
// The LayoutUpdated event for the DataGrid
private void myDataGrid_LayoutUpdated(object sender, EventArgs e)
{
if (myDataGrid.SelectedItem == null)
return;
//<----------
// may become improved to check first if the `ScrollIntoView()` is really needed
// To prevent hanging here the ItemsSource must be
// a) an ObervableCollection with a correct working binding or
// b) myDataGrid.Items.Refresh(); must be called after changing
// the data
myDataGrid.ScrollIntoView(myDataGrid.SelectedItem, null);
}
this works for me:
public class ScrollingDataGrid : DataGrid
{
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
var grid = e.Source as DataGrid;
if(grid.SelectedItem != null)
{
grid.UpdateLayout();
grid.ScrollIntoView(grid.SelectedItem, null);
}
base.OnSelectionChanged(e);
}
}
Related
I want to deny deletion of a row in case some there is a property with specific value, for example if product type is Steel I would like to deny user from deleting that row..
I'm setting source to my datagrid like this:
dataGridSourceList = new ObservableCollection<DatabaseItems>(TempController.Instance.SelectItemsByUserId(Globals.CurrentUser.Id));
dtgMainItems.ItemsSource = dataGridSourceList;
I saw there is a property CanUserDeleteRows
And I've added this to definition of my datagrid in xaml but I'm not sure how to apply this properly..
CanUserDeleteRows="{Binding ElementName=dtgMainItems, Path=SelectedItem.IsDeleteEnabled}"
Any kind of help would be awesome
Thanks
You could handle the CommandManager.PreviewCanExecute attached event:
private void OnPreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (e.Command == DataGrid.DeleteCommand)
{
DatabaseItems selectedItem = dtgMainItems.SelectedItem as DatabaseItems;
if (selectedItem != null && !selectedItem.IsDeleteEnabled)
e.Handled = true;
}
}
XAML:
<DataGrid x:Name="dtgMainItems" CommandManager.PreviewCanExecute="Grid_PreviewCanExecute" />
I have this 2 datagrid, on selectionchanged in first datagrid it will load the data base in the id i passed in second datagrid
this is the code from first datagrid in selectionchanged event
private void equipmentDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
var row_list = (Equipment)equipmentDataGrid.SelectedItem;
var inspectionContent = new InspectionContent();
inspectionContent.PassingValue(row_list.ID);
}
catch {
}
}
InspectionContent is the another usercontrol w/c contains the second datagrid. i passed a id value to load the data in second datagrid, my problem was the loading part is not called in the constructor, why is it the data in second datagrid is not loading?
You have to use the event args if you want to proceed in this way with the code behind
DataGrid dataGrid = sender as DataGrid;
if (sender != null && e.AddedItems.Count > 0) {
var row = e.AddedItems[0] as Equipment;
if (row != null)
{
inspectionContent.PassingValue(row.ID);
Alternatively, for the future, you may want to study the MVVM approach that is based on property binding ...
Please note also that your try/catch is very bad, since it silently discard any exception, at least track the error in debug
catch (Exception exc) {
Debug.WriteLine (exc.Message + exc.StackTrace);
}
This creates a new instance of the InspectionContent UserControl:
var inspectionContent = new InspectionContent();
You want to call the PassingValue of the already created instance that you see on the screen.
Give it a name ("uc" in the below sample) in your XAML markup:
<local:InspectionContent x:Name="uc" ...>
...and access it using this name in the event handler:
private void equipmentDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var row_list = (Equipment)equipmentDataGrid.SelectedItem;
uc.PassingValue(row_list.ID);
}
I have the following DataGrid
<DataGrid CanUserDeleteRows="True"
CanUserAddRows="True"
SelectedItem="{Binding SelectedResource, Mode=TwoWay}"
ItemsSource="{Binding Path=Resources, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
IsAsync=True}"> ... </<DataGrid>
I am using the MVVM pattern to bind to an ObservableCollection<ResourceViewModel> Resources and this works great. I have a button that adds a new row and this is done by adding a new ResourceViewModel to the Resources collection - great. Now, I want the user to be able to click on the empty last row, and this automatically creates a new record in the DataGrid.
I have made sure the DataGrid has CanUserAddRows=True. I have made sure the class in the collection Resources (ResourceViewModel) that I am binding to has a default constructor (no parameters) and I have made sure the collection type is not readonly. When the user clicks on the last row the default constructor fires but to correctly instantiate the new ResourceViewModel object in need a reference to either the grid of the Resources collection...
I suppose I could use and AttachedCommand on the CellBeginEdit event and then add a new ResourceViewModel to the observable collection there, is there a standard way of doing this?
Note, I have read the following questions and these are of no help to me
WPF DataGrid - Event for New Rows?
How to add rows with a DataGrid and MVVM
Edit. It turns out that I am having problems doing this due to a bug in the WPF DataGrid. See Nigel Spencer's Blog. However, his fix is not working for me at the moment...
As far as I understand, you know how to correctly add new items into your data bound collection to result in a new item being added to the DataGrid and your question actually relates to how can you do this when the user clicks on the last row in the DataGrid. The general way to handle some event in the view model is to create an Attached Property (if one does not already exist) that handles that event for you.
For instance, you could create an Attached Property that attaches a handler to the relevant event and another of type ICommand which you could execute when the event handler is called. Then you could write the functionality of that ICommand in your view model (in which you add a new item to your data bound collection) and data bind your ICommand implementation to the Attached ICommand Property.
As I'm fairly sure you know how to define Attached Propertys, I won't insult you by showing you. Please let me know if I have misunderstood you, or not made myself clear.
Here's an attached property that registers a command for adding rows (assuming the source collection contains a generic type argument):
public static readonly DependencyProperty RegisterAddCommandProperty = DependencyProperty.RegisterAttached("RegisterAddCommand", typeof(bool), typeof(DataGridExtensions), new PropertyMetadata(false, OnRegisterAddCommandChanged));
public static bool GetRegisterAddCommand(DependencyObject obj)
{
return (bool)obj.GetValue(RegisterAddCommandProperty);
}
public static void SetRegisterAddCommand(DependencyObject obj, bool value)
{
obj.SetValue(RegisterAddCommandProperty, value);
}
static void OnRegisterAddCommandChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (sender is DataGrid)
{
var DataGrid = sender as DataGrid;
if ((bool)e.NewValue)
DataGrid.CommandBindings.Add(new CommandBinding(AddCommand, AddCommand_Executed, AddCommand_CanExecute));
}
}
public static readonly RoutedUICommand AddCommand = new RoutedUICommand("AddCommand", "AddCommand", typeof(DataGridExtensions));
static void AddCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
var DataGrid = sender as DataGrid;
var ItemsSourceType = DataGrid.ItemsSource.GetType();
var ItemType = ItemsSourceType.GetGenericArguments().Single();
DataGrid.Items.Add(Activator.CreateInstance(ItemType));
}
static void AddCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (sender as DataGrid).CanUserAddRows;
}
And then you can apply the command to a button somewhere like this:
<Button Command="{x:Static Extensions:DataGridExtensions.AddCommand}"/>
Don't forget to specify the command target.
I am using a ComboBox to insert a text template into a RichEdit control (the name of the template is in the picklist for the ComboBox.)
It all works great except when the user selects the same value in the list again. Then the SelectionChanged is not firing. That makes sense based on the name of the event (SelectionChanged), but I need to know that the value was re-selected so I can insert it again.
Is there a way to know that an item was re-selected from the ComboBox? (Or a better control to use?)
I tried using the DropDownClosed event, but that fires even if the item was not re-selected. (They open the drop down then click on another control.)
It sounds like the way you are using your combo box isn't consistent with normal usage. Would it work to have a button next to the combo box that inserted the selected template. I think that behavior would work better for users who are familiar with the behavior of Google search with search sugestions
I had the same question and I finally found the answer:
You need to handle BOTH the SelectionChanged event and the DropDownClosed like this:
In XAML:
<ComboBox Name="cmbSelect" SelectionChanged="ComboBox_SelectionChanged" DropDownClosed="ComboBox_DropDownClosed">
<ComboBoxItem>1</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
</ComboBox>
In C#:
private bool handle = true;
private void ComboBox_DropDownClosed(object sender, EventArgs e) {
if(handle)Handle();
handle = true;
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
ComboBox cmb = sender as ComboBox;
handle = !cmb.IsDropDownOpen;
Handle();
}
private void Handle() {
switch (cmbSelect.SelectedItem.ToString().Split(new string[] { ": " }, StringSplitOptions.None).Last())
{
case "1":
//Handle for the first combobox
break;
case "2":
//Handle for the second combobox
break;
case "3":
//Handle for the third combobox
break;
}
}
The best I could find was to clear out the selected value as the dropdown opens. This is not ideal as the user does not get to keep their previous location as a point of reference (good for long lists). But it is the best solution I can find.
Here is the code I used:
ctor()
{
myComboBox.DropDownOpened += OnDropDownOpened;
}
private void OnDropDownOpened(object sender, EventArgs e)
{
var comboBox = ((ComboBox)sender);
comboBox.SelectedItem = null;
}
I would use a list box. The user does not need to open it. Instead he can immediately select an item from the list. Also I would not attach this function to the SelectionChanged event but to the MouseDoubleClick event. This allows selecting and reselecting an item easily. Also I would add a button, which triggers the same function. The double click should only be a kind of shortcut for this button, also the button can have a descriptive text and/or an icon, which makes it even more intuitive.
SelectionChanged will fire even when the user moves up and down using the arrow keys, but the function should not be triggered then.
You'll need to provide some code to show what you're trying to do. If an item is already selected, why would the RichEdit control's template not already be set anyway? When dealing with WPF it helps to get familiar with binding to view models, not just for things like ItemsSource but also SelectedItem. Given the scenario you describe I would use a binding for the SelectedItem to the View Model then bind the RichEdit control's template to the same View Model property, using a value converter if necessary. This way you don't need to mess around with click events and the like. Provided the View Model's property is a DependencyProperty or fires a PropertyChanged event (see INotifyPropertyChanged) your RichEdit control's template should automatically reflect the selection in the drop-down.
** Edit **
Based on your comments I'm assuming the behaviour you want is to set text based on a combo selection but allow the user to customise that text. However, if edited they should be able to re-select the combo value to reset the text. The trouble is that if the item is already selected then there is no event fired to hook into. The solution is that if the text contents change, this should de-select any combo selection (since the combo no longer reflects the contents of the text box.) Bindings can manage this quite nicely:
This example view uses a TextBox for simplicity:
<Window x:Class="UISample.UITemplateSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UITemplateSample" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ComboBox ItemsSource="{Binding Path=Templates}" SelectedItem="{Binding Path=SelectedTemplate}" DisplayMemberPath="Name"/>
<TextBox Grid.Row="1" AcceptsReturn="True" TextWrapping="Wrap" Text="{Binding Path=Text}"/>
</Grid>
The ViewModel:
class TemplateSampleViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<TextTemplate> Templates
{
get;
private set;
}
private TextTemplate _selectedTemplate;
public TextTemplate SelectedTemplate
{
get{ return _selectedTemplate; }
set
{
if ( _selectedTemplate == value )
return;
_selectedTemplate = value;
if (_selectedTemplate != null)
Text = _selectedTemplate.TemplateText;
firePropertyChanged("SelectedTemplate");
}
}
private string _text;
public string Text
{
get { return _text; }
set
{
if ( _text == value )
return;
_text = value;
firePropertyChanged( "Text" );
var matchingTemplate = Templates.FirstOrDefault( t => t.TemplateText == _text );
SelectedTemplate = matchingTemplate;
}
}
public TemplateSampleViewModel(IEnumerable<TextTemplate> templates)
{
Templates = new ObservableCollection<TextTemplate>(templates);
}
private void firePropertyChanged(string propertyName)
{
if ( PropertyChanged != null )
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And wiring it up:
var viewModel = new TemplateSampleViewModel(new[]
{
new TextTemplate {Name = "First", TemplateText = "This is the first item"},
new TextTemplate {Name = "Second", TemplateText = "This is the second item"},
new TextTemplate {Name = "Third", TemplateText = "This is the third item"},
});
var test = new UITemplateSample {DataContext = viewModel};
test.Show();
This binds the combo box, then as items are selected, the text box is updated automatically. When the text box contents change, the template is inspected to see if it still matches and if not, the combo item is de-selected. If the entry matches a template then that template is selected automatically.
C#, .NET 4.0, VS2010.
New to WPF. I have a ComboBox on my MainWindow. I hooked the SelectionChanged event of said combo box. However, if I examine the value of the combo box in the event handler, it has the old value. This sounds more like a "SelectionChanging" event, than a SelectionChanged event.
How do I get the new value of the ComboBox after the selection has actually happend?
Currently:
this.MyComboBox.SelectionChanged += new SelectionChangedEventHandler(OnMyComboBoxChanged);
...
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = this.MyComboBox.Text;
}
Note, I get the same behaviour if I use the object being passed in the event args, e.g. e.OriginalSource.
According to MSDN, e.AddedItems:
Gets a list that contains the items that were selected.
So you could use:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = (e.AddedItems[0] as ComboBoxItem).Content as string;
}
You could also use SelectedItem if you use string values for the Items from the sender:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = (sender as ComboBox).SelectedItem as string;
}
or
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;
}
Since both Content and SelectedItem are objects, a safer approach would be to use .ToString() instead of as string
The correct value to check here is the SelectedItem property.
A ComboBox is a composite control with two of its parts being:
The Text Part: the value in the this part corresponds to the Text property of the ComboBox.
The Selector Part (i.e. the "drop-down" part): The selected item in this part corresponds to the SelectedItem property.
The image above was taken immediately after the ComboBox was expanded (i.e. before selecting a new value). At this point both Text and SelectedItem are "Info", assuming the ComboBox items were strings. If the ComboBox items were instead all the values of an Enum called "LogLevel", SelectedItem would currently be LogLevel.Info.
When an item in the drop-down is clicked on, the value of SelectedItem is changed and the SelectionChanged event is raised. The Text property isn't updated yet, though, as the Text Part isn't updated until after the SelectionChanged handler is finished. This can be observed by putting a breakpoint in the handler and looking at the control:
Since the Text Part hasn't been updated at this point, the Text property returns the previously selected value.
Use the DropDownClosed event instead of selectionChanged if you want the current value of the combo box.
private void comboBox_DropDownClosed(object sender, EventArgs e)
{
MessageBox.Show(comboBox.Text)
}
Is really that simple.
This worked for me:
private void AppName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem cbi = (ComboBoxItem)AppName.SelectedItem;
string selectedText = cbi.Content.ToString();
}
This worked for me:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
var text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;
}
Following event is fired for any change of the text in the ComboBox (when the selected index is changed and when the text is changed by editing too).
<ComboBox IsEditable="True" TextBoxBase.TextChanged="cbx_TextChanged" />
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
string newItem = ((DataRowView) e.AddedItems[0]).Row.ItemArray[0].ToString();
}
The second option didn't work for me because the .Text element was out of scope (C# 4.0 VS2008). This was my solution...
string test = null;
foreach (ComboBoxItem item in e.AddedItems)
{
test = item.Content.ToString();
break;
}
If you really need the SelectionChanged event, then the best answer is SwDevMan81's answer. However, if you are starting with WPF, then you might want to learn how to do things the WPF way, which is different than the old Windows Forms days that used to rely on events like SelectionChanged, with WPF and Model View ViewModel pattern, you should use bindings. Here is a code example:
// In the Views folder: /Views/MyWindow.xaml:
// ...
<ComboBox ItemsSource="{Binding MyViewModel.MyProperties, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem="{Binding MyViewModel.MyProperty , RelativeSource={RelativeSource AncestorType=Window}}" />
// ...
// In the Views folder: /Views/MyWindow.xaml.cs:
public partial class MyWindow : Window
{
public MyViewModelClass MyViewModel {
get { return _viewModel; }
private set { _viewModel = value;}
}
public MyWindow()
{
MyViewModel.PropertyChanged += MyViewModel_PropertyChanged;
}
void MyViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "MyProperty")
{
// Do Work
// Put your logic here!
}
}
}
using System.ComponentModel;
// In your ViewModel folder: /ViewModels/MyViewModelClass.cs:
public class MyViewModelClass : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation:
private void NotifyPropertyChanged(string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
public event PropertyChangedEventHandler PropertyChanged;
// Selected option:
private string _myProperty;
public string MyProperty {
get { return _myProperty; }
set { _myProperty = value; NotifyPropertyChanged("MyProperty"); }
}
// Available options:
private List<string> _myProperties;
public List<string> MyProperties {
get { return _myProperties; }
set { _myProperties = value; NotifyPropertyChanged("MyProperties"); }
}
}
private void indBoxProject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int NewProjID = (e.AddedItems[0] as kProject).ProjectID;
this.MyProject = new kProject(NewProjID);
LoadWorkPhase();
}
The use of the e.AddedItems[0] as kProject where kProject is a class that holds the data worked for me as it was defaulting to the RemovedItems[0] before I have made this explicit distinction. Thanks SwDevMan81 for the initial information that answered this question for me.
I needed to solve this in VB.NET. Here's what I've got that seems to work:
Private Sub ComboBox1_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles ComboBox_AllSites.SelectionChanged
Dim cr As System.Windows.Controls.ComboBoxItem = ComboBox1.SelectedValue
Dim currentText = cr.Content
MessageBox.Show(currentText)
End Sub
It's weird that SelectedItem holds the fresh data, whereas SelectedValue doesn't. Sounds like a bug to me. If your items in the Combobox are objects other than ComboBoxItems, you will need something like this: (my ComboBox contains KeyValuePairs)
var selectedItem = (KeyValuePair<string, string>?)(sender as ComboBox).SelectedItem;
if (!selectedItem.HasValue)
return;
string selectedValue = selectedItem.Value.Value; // first .Value gets ref to KVPair
ComboBox.SelectedItem can be null, whereas Visual Studio keeps telling me that a KeyValuePair can't be null. That's why I cast the SelectedItem to a nullable KeyValuePair<string, string>?. Then I check if selectedItem has a value other than null. This approach should be applicable to whatever type your selected item actually is.
Don't complicate things for no reason. Using SelectedValue property you can easily get a selected ComboBox value like this: YourComboBoxName.SelectedValue.ToString().
Behind the scene the SelectedValue property is defined as: SelectedValue{get; set;} this means you can use it to get or set the value of a ComboBox.
Using SelectedItem is not an efficient way to get a ComboBox value since it requires a lot of ramifications.
You can check SelectedIndex or SelectedValue or SelectedItem property in the SelectionChanged event of the Combobox control.
From SelectionChanged event of a combobox you can get the selected item text as follow:
private void myComboBox_SelectionChanged (object sender, SelectionChangedEventArgs e)
{
ComboBoxItem comboBoxItem = (ComboBoxItem) e.AddedItems[0];
string selectedItemText = comboBoxItem.Content.ToString();
}
clean and easy - TextChanged
private void ComboBox1_TextChanged(object sender, EventArgs e)
{
MessageBox.Show(ComboBox1.Text);
}
Following works for me to get current selection of ComboBox (c# / .net6):
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
string? text = ((ComboBoxItem)(sender as ComboBox).SelectedItem).Content as string;
if(text != null){
...
}
}
This should work for you ...
int myInt= ((data)(((object[])(e.AddedItems))[0])).kid;
I solved this by using the DropDownClosed event because this fires slightly after the value is changed.