BindingList and ListBox behaviour - c#

I have a ListBox that is bound to a BindingList collection. This works great.
My only grief occurs when the first item enters the collection. The default behavior of the ListBox is to select that item - yet, this does not raise the SelectedIndexChanged event. I assume this is because the SelectedIndex is initially set to null; on becoming anything but null, the index isn't actually changed; rather assigned. How can I stop the default behavior of selecting (highlighting) the first initial item added to the ListBox?
If my assumption is wrong, please shed some light?
Update
Here is the core parts of my code thus far.
public UavControlForm()
{
InitializeComponent();
_controlFacade = new UavController.Facade.ControlFacade();
UpdateFlightUavListBox();
}
private void UpdateFlightUavListBox()
{
lsbFlightUavs.DataSource = _controlFacade.GetFlightUavTally();
lsbFlightUavs.DisplayMember = "Name";
}
private static BindingList<FlightUav> _flightUavTally = new BindingList<FlightUav>();
public BindingList<FlightUav> FlightUavTally
{
get { return _flightUavTally; }
}
public void AddFlightUav(double[] latLonAndAlt)
{
FlightUav flightUav = new FlightUav();
flightUav.Latitude = latLonAndAlt[0];
flightUav.Longitude = latLonAndAlt[1];
flightUav.Altitude = latLonAndAlt[2];
_flightUavTally.Add(flightUav);
UtilStk.InjectAircraftIntoStk(flightUav.Name);
flightUav.SetFlightDefaults();
PlayScenario();
}
Update:
So, setting lsbFlightUavs.SelectedIndex = -1 solves the problem. The above method AddFlightUav() is called from a button's OnClick handler in a second form from the main form. How can I call lsbFlightUavs.SelectedIndex = -1 from this second form when the AddFlightUav() method returns? I know I can make the ListBox static, but that seems like bad practice to me.. what would be a more elegant solution?

Using WinForms, I implemented the Singleton pattern. This enabled me to access the ListBox control from my second form.
Form 1
private static UavControlForm _instance = new UavControlForm();
private UavControlForm()
{
InitializeComponent();
}
public static UavControlForm Instance
{
get { return _instance; }
}
public ListBox FlightUavListBox
{
get { return lsbFlightUavs; }
}
Form 2
UavControlForm.Instance.FlightUavListBox.SelectedIndex = -1;

Related

set IsEnabled = false a button in silverlight

I have the following code in the ViewModel class, in the constructor where I define that the buttons are always Enabled = false when starting the form ...
public partial class EditarConceptoWindow : ChildWindow
{
public EditarConceptoWindow(string documentoId)
{
InitializeComponent();
viewModel.Saved += new EventHandler<Microsoft.Practices.Prism.Events.DataEventArgs<bool>>(ViewModel_Saved);
viewModel.Calculation += new EventHandler<Microsoft.Practices.Prism.Events.DataEventArgs<bool>>(ViewModel_Calculation);
this.DataContext = viewModel;
BtnCalcular.IsEnabled = false;
BtnObtenerTCRM.IsEnabled = false;
....... rest of code
In a checked event of a check box when placing the Selected check box, it must be enabled to be set to true, depending on whether a particular element of a combobox has been selected as well;
private void cbAgregarManual_Checked(object sender, RoutedEventArgs e)
{
if (this.ComboConcepto.SelectedValue.ToString() == "DPI")
{
BtnCalcular.IsEnabled = true;
BtnObtenerTCRM.IsEnabled= true;
}
}
This must be done if and only if the checkbox is clicked and the DPI value is selected in the combobox.
But the behavior of the buttons is that when starting the form they are always IsEnabled = true and if the checkbox control is clicked if it works but I can't find a reason because only until I click the checkbox it works, there are some controls (such as TextBoxes, and also the buttons) with this directive in the XAML.
IsEnabled="{Binding ElementName=cbAgregarManual, Path=IsChecked }"
I suggest that you centralize the logic of the enabling into one property to avoid this mismatch of logic setting confusion.
In this new property it will use INotifyPropertyChanged for the notification of that change, but called in from other properties. So to sum up, when any of the associated values change, they do the notify call on the logic property; that process ensures that the control is properly en/dis-abled.
Example
Such as this pseudo code where I check three different other properties:
public bool IsEnabledCombo { get { return ClickStatus
&& OtherStatus
&& !string.IsNullOrEmpty( UserText); }
public bool ClickStatus { get { return _clickStatus; }
set { _clickStatus = value;
NotifyPropertyChanged("ClickStatus");
NotifyPropertyChanged("IsEnabledCombo");
}}
public bool OtherStatus { get { return _otherStatus; }
set { _clickStatus = value;
NotifyPropertyChanged("OtherStatus");
NotifyPropertyChanged("IsEnabledCombo");
}}
public string UserText { ...
set { _userText = value;
NotifyPropertyChanged("UserText");
NotifyPropertyChanged("IsEnabledCombo");
Bind your control as such
IsEnabled="{Binding IsEnabledCombo }"
So wherever one of the related values can change they also call NotifyPropertyChanged("IsEnabledCombo"); and the control status is updated automatically.
I provide a another notify chained example doing similar on my blog:
Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding

Make ComboBox accept only specific type

I currently have a ComboBox in my Windows Forms Application. In order to specify which values the ComboBox will contain, I set DataSource property of the ComboBox to some array so that ComboBox contains values from that array. I could also use Items.Add() to add new values to ComboBox. However, I want to make sure that ComboBox can be populated with objects of some specific type. So, if I have a class called X, then I want to make it so that only an array of type X can be used as a data source for the ComboBox. Right now, ComboBox accepts objects of type System.Object. How can I achieve it? Is there a property of ComboBox that I need to set to be equal to my data type's name? Or is there an event that will check whether an object added to my ComboBox is of the needed type and will throw an exception if not?
I was thinking of creating a new class as a subtype of ComboBox, and overriding the Add method of Items property so that Add checks whether its argument is of the needed type (not sure if and how I can do it). Even if I do that, there are still other ways to add new values into ComboBox (AddRange, CopyTo, etc.), so I think there should be a more elegant solution to this problem.
If you want to control the type of item that the ComboBox can contain, you could try creating a new class derived form ComboBox, but you'd run into the problem that it still has the ComboBox.ObjectCollection Items property which would still accept any type! And (unfortunately for your idea of overriding) the Add method isn't virtual.
The only practical solution that I could think of would be to abstract the ComboBox somehow. If this isn't shared code, I would recommend just creating a method that you would use to add items to the ComboBox. Something like:
// NOTE: All items that are added to comboBox1 need to be of type `SomeType`.
private void AddItemToComboBox(SomeType item)
{
comboBox1.Items.Add(item);
}
Any attempt to add a non-SomeType object to the ComboBox would be met with a compiler error. Unfortunately, there's no easy way to prevent someone from still adding a non-SomeType item to ComboBox.Items directly.
Again, if this isn't shared code, it shouldn't really be an issue.
You can hide Items property by your
own Items property of custom type which taking as parameter original ItemsCollection
Example class for testing
public class Order
{
public Int32 ID { get; set; }
public string Reference { get; set; }
public Order() { }
public Order(Int32 inID, string inReference)
{
this.ID = inID;
this.Reference = (inReference == null) ? string.Empty : inReference;
}
//Very important
//Because ComboBox using .ToString method for showing Items in the list
public override string ToString()
{
return this.Reference;
}
}
With next class I tried wrap ComboBox's items collection in own type.
Where adding items must be concrete type
Here you can add other methods/properties you need (Remove)
public class ComboBoxList<TCustomType>
{
private System.Windows.Forms.ComboBox.ObjectCollection _baseList;
public ComboBoxList(System.Windows.Forms.ComboBox.ObjectCollection baseItems)
{
_baseList = baseItems;
}
public TCustomType this[Int32 index]
{
get { return (TCustomType)_baseList[index]; }
set { _baseList[index] = value; }
}
public void Add(TCustomType item)
{
_baseList.Add(item);
}
public Int32 Count { get { return _baseList.Count; } }
}
Here custom combobox class derived from ComboBox
Added: generic type
public class ComboBoxCustomType<TCustomType> : System.Windows.Forms.ComboBox
{
//Hide base.Items property by our wrapping class
public new ComboBoxList<TCustomType> Items;
public ComboBoxCustomType() : base()
{
this.Items = new ComboBoxList<TCustomType>(base.Items);
}
public new TCustomType SelectedItem
{
get { return (TCustomType)base.SelectedItem; }
}
}
Next code used in the Form
private ComboBoxCustomType<Order> _cmbCustom;
//this method used in constructor of the Form
private void ComboBoxCustomType_Initialize()
{
_cmbCustom = new ComboBoxCustomType<Order>();
_cmbCustom.Location = new Point(100, 20);
_cmbCustom.Visible = true;
_cmbCustom.DropDownStyle = ComboBoxStyle.DropDownList;
_cmbCustom.Items.Add(new Order(0, " - nothing - "));
_cmbCustom.Items.Add(new Order(1, "One"));
_cmbCustom.Items.Add(new Order(2, "Three"));
_cmbCustom.Items.Add(new Order(3, "Four"));
_cmbCustom.SelectedIndex = 0;
this.Controls.Add(_cmbCustom);
}
Instead of overriding ComboBox (which wont work as stated in itsme86's answer) you could override usercontrol, add a combobox to this, and then only expose the elements that you wish to work with. Something similar to
public partial class MyComboBox<T> : UserControl where T: class
{
public MyComboBox()
{
InitializeComponent();
}
public void Add(T item)
{
comboBox1.Items.Add(item);
}
public IEnumerable<T> Items
{
get { return comboBox1.Items.Cast<T>(); }
}
}
Please note however that some pieces of automated software rely on access the the underlying controls however so this may cause some issues.
This approach never changes the Items of the combobox so they will still store as objects but when you access them, you are casting them to the correct type and only allowing them to be added of that type. You can create a new combobox via
var myCB = new MyComboBox<ItemClass>();

C# WPF Open a window, but checkboxes are not set

i have a problem with my Checkboxes.
I have a MainWindow(), from here i open my second Window to set some searchpropertys for the user.
(in wich fields they want to search ... like Name, Firstname and other)
After the user confirmed his choices, the Window get closed and the values get saved in the app.propertys.
On the next start up of the application the values from the session before are still how they should be, so this part works.
Now the problem:
If i now open the Window all the Checkboxes arnt set like they should be?!
I have them bound to my ViewModel like this :
<CheckBox x:Name="cVersichertenstatus" Content=... **IsChecked="{Binding Versichertenstatuschecked}"** .../>
The code for my bindings lock like this:
private bool versichertenstatusischecked;
public bool Versichertenstatuschecked
{
get
{
return versichertenstatusischecked;
}
set
{
if (Versichertenstatuschecked != value)
{
versichertenstatusischecked = value;
//this.OnPropertyChanged("Versichertenstatuschecked");
}
}
}
After the start up i set all the binding propertys with the values of the app.propertys.
But it change nothing. The Checkboxvalues are always false :-(
I guess its because i dont use OnPropertyChanged, but if i try i get this error:
object reference not set to an instance of the object
I dont get it, i have alot of bindings and all work fine, only the checkbox things do not.
Can someone explain me what i have to do to resolve this error?
Edit:
Set the Datacontext :
public MainWindow()
{
InitializeComponent();
SetupBindings();
var l = new app_config_load();
l.load();
}
private void SetupBindings()
{
pViewModelList viewModel = new pViewModelList();
plistview.DataContext = viewModel;
}
and the load() class:
public void load()
{
Properties.Settings get = new Properties.Settings();
pViewModelList a = new pViewModelList();
a.Nachnamechecked = get.cnachname_app;
a.Versichertenstatuschecked = get.cversichertenstatus_app;
a.Geburtsdatumchecked = get.cgeburtsdatum_app;
a.Versicherungsnrchecked = get.cversichertennummer_app;
a.Vornamechecked = get.cvorname_app;
get.Save();
}
Do i have to initialize somehow the Propertys first? Thought this happens automaticly ?!
Did you set the DataContext?
public MyWindow(MyClass myClass){
InitializeComponent();
this.DataContext = myClass;
}
Before InitializeComponent(); all of your GUI - Elements will be null!
How to get your Element from MainWindow:
public MyClass GetData()
{
MyClass data = this.DataContext as MyClass;
return data;
}
and try:
IsChecked="{Binding Path=Versichertenstatuschecked}"
I think I know what the problem is. You miss the Mode of the binding.
IsChecked="{Binding Versichertenstatuschecked, Mode=TwoWay}"
Using this will trigger changes in the viewModel to be displayed in the view.
i solved the problem by my self.
I just needed to wait till the Window is loaded before i use my Class to set the bindingpropertys.
I used the "loaded" event to call the Class and jeah, no longer any Object Reference errors :)
Such things are realy frustrating.
Cya

Binding a DataGridView to a Object list

I'm new to Databinding in .NET and I want to bind my DataGridView to a object list. This grid may populate in two ways.
There are TextBoxes in the form and users can enter text and when they press Add button a new object is instantiated with the provided fields and it is added to the list.
Other way is, when a user searches for objects, matching objects are loaded from database to the list. Also after searching users should be able to add new items to this list as in the first method.
Finally this list is binded to the DataGridView and now user could do any changes to the items shown in the grid and if it's all right, user could save them.
So in my presenter I have three methods for above requirements.
Class AttendancePresenter
{
private void AddAttendance()
{
AttendanceList.Add(attendanceModel);
var bindingList = new BindingList<IAttendance>(AttendanceList);
var source = new BindingSource(bindingList, null);
_View.AttendanceGrid = source;
}
private void GetAttendance()
{
AttendanceList = _DataService.GetAttendance();
var bindingList = new BindingList<IAttendance>(AttendanceList);
var source = new BindingSource(bindingList, null);
_View.AttendanceInGrid = source;
}
private void Save()
{
_DataService.InsertAttendance (AttendanceList);
}
}
I'm not sure the way I've binded things is right. And also I wonder if I could use a single binding list here as I'm binding same list to the same DataGridView! Is it possible?
Whats the difference of using BindingSource along and with a BindingList, in this context?
Bind the DataGridView to the presenters AttendanceList right in the constructor, assuming your view is instantiated at that point. Then in the presenter do something like this:
Class AttendancePresenter
{
private readonly BindingList<IAttendance> _attendanceList;
public AttendancePresenter()
{
_attendanceList = new BindingList<IAttendance>();
_View.AttendanceGrid = _attendanceList;
}
private void AddAttendance()
{
_attendanceList.Add(attendanceModel);
}
private void GetAttendance()
{
_attendanceList.Clear();
var attendance = _DataService.GetAttendance();
foreach (var attendant in attendance)
{
_attendanceList.Add(attendant);
}
}
private void Save()
{
_DataService.InsertAttendance (_attendanceList);
}
}

Binding ListBox.SelectedItem to Property

This might be a duplicate question, but I'm unable to find a good answer. All the answers like Binding WinForms ListBox to object properties don't work on my WinForm. I'll explain.
I have a list of Firms that I show in a ListBox. I would like when the SelectedItem changes, that it updates a property on my model. So that I can read the Firms properties.
// the classes
public class Firm
{
public string Name { get; set; }
public int Id { get; set; }
// more properties ...
}
public class MyModel : INotifyPropertyChanged
{
private Firm _firm = new Firm();
public Firm Firm
{
get { return _firm; }
set
{
if (Equals(value, _firm)) return;
_firm = value;
OnPropertyChanged();
}
}
// more properties and OnPropertyChanged() ...
}
// the form
private MyModel Model;
public void MyForm(List<Firm> firms)
{
lstFirm.DataBindings.Add("SelectedItem", Model, "Firm",
true, DataSourceUpdateMode.OnPropertyChanged);
lstFirm.DisplayMember = "Name";
lstFirm.ValueMember = "Id";
lstFirm.DataSource = firms;
}
public void lstFirm_SelectedIndexChanged(object sender, EventArgs e)
{
// Do something with Model.Firm
}
The problem is that Model.Firm null is. Does anybody have an idea what I need to do to make a databinding between the ListBox and the Model? I bind other stuff on my WinForm (such as TextBoxes to String properties) and those work nicely.
From what I can see, your code never sets Model.Firm... Where's the constructor for MyModel? If you don't provide one, Model.Firm will stay null unless you explicitly set it. Here's an example constructor:
public MyModel(Firm firm)
{
_firm = firm;
}
Also, Equals() doesn't do what you think it does. Instead of if (Equals(value, _firm)) return;, use this: if (value == _firm) return;
Ok, so after a weekend of testing, I figured it out.
I was debuging in the SelectedIndexChanged event and didn't see the change in my Model.Firm just yet. But as the SelectedItemChanged event is only internal, I couldn't use that and that's where the databinding on SelectedItem applies the values to databound items.
Now the reason why the change isn't visible yet, is because the SelectedItemChanged is only fired after the SelectedIndexChanged is executed. So internally in the ListBox control, it probably looks like
this.SelectedIndex = value;
this.SelectedItem = FindItem(value);
this.SelectedIndexChanged(/*values*/);
this.SelectedItemChanged(/*values*/); // Apply databinding changes
So it's quite normal that you don't see the changes, before the change has occured. And I didn't know this, so I was kinda stumped why the SelectedItem (who was displaying the changed value) wasn't copied over to the databound model property.
So I didn't have to change anything major to get it all working. :)

Categories