I am working on a C# project which includes WPF. I was wondering, If I could somehow check If my data grid contains certain element.
For example,
I have combo box whose itemsSource is some list of objects. Now, when an user selects an item from the combo box and presses the button
below in data grid (in same window) that item shows up.
I want to forbid the user to select same item more than once and for example put MessageBox with error message. How could I do that?
Code
This Window:
public partial class AvioWindowAddNEdit : Window
{
Avio avio;
public enum Stage { ADD, EDIT};
Stage stage;
public AvioWindowAddNEdit(Avio avio, Stage stage = Stage.ADD)
{
InitializeComponent();
this.avio= avio;
this.stage= stage;
textboxCode.DataContext = avio;
comboboxListofFlights.ItemsSource = Aplikacija.Instance.Flights;
comboboxListofFlights.DataContext = avio;
datagridListofFlights.ItemsSource = avio.ListofFlights;
datagridListofFlights.ColumnWidth = new DataGridLength(1, DataGridLengthUnitType.Auto);
if (stage== Stage.EDIT)
{
textboxCode.IsEnabled = false;
}
}
}
Button which adds selected item to data grid:
private void btnAddFlight_Click(object sender, RoutedEventArgs e)
{
avio.ListOfFlights.Add(comboboxListOfFlights.SelectedItem as Flight);
}
Singleton class for loading in all of my data:
class Aplication
{
public ObservableCollection<User> Users { get; set; }
public ObservableCollection<Airport> Airports { get; set; }
public ObservableCollection<Flight> Flights{ get; set; }
public ObservableCollection<Avio> Avios { get; set; }
public string LoggedInUser { get; set; }
private static Aplication instance = new Aplication();
public static Aplication Instance
{
get
{
return instance;
}
}
private Aplication()
{
Users= new ObservableCollection<User>();
Airports = new ObservableCollection<Airport>();
Flights = new ObservableCollection<Flight>();
Avios= new ObservableCollection<Avio>();
FillInData(); //method where I filled in all of these ObservableCollections
}
}
My class:
public class Avio : ObservableObject, ICloneable
{
//observableobject is an object where I implemented INotifyPropertyChanged
private string code;
public string Code
{
get { return code; }
set { code= value; OnPropertyChanged("Code"); }
}
private ObservableCollection<Flight> listOfFlights;
public ObservableCollection<Flight> ListOfFlights
{
get { return listOfFlights; }
set { listOfFlights= value; OnPropertyChanged("ListOfFlights"); }
}
private bool active;
public bool Active
{
get { return active; }
set { active= value; OnPropertyChanged("Active"); }
}
public Avio()
{
active= true;
ListOfFlights = new ObservableCollection<Flight>();
}
public Avio(string code)
{
active= true;
ListOfFlights = new ObservableCollection<Flight>();
Code= code;
}
}
You could use an ObservableCollection as an ItemsSource for your DataGrid. In that way you'll always have easy access to the data via code.
Check out this tutorial as a starting point (this uses ListBox instead of DataGrid, but it's easily adaptable to DataGrid).
Related
I am in trouble while working on Databinding in windows form. I have two classes, one is Project and another is Update. Now all project object is having a list of Updates and it is binded to a combobox, but when the user changes selection need to display/bind the properties of Update object to another controls. But is not updating as expected, when the user changes selection. Please help me on this..
Screenshot
See my class and codes below,
public class Project
{
private int _id;
private string _name;
public Project(int id, string name)
{
_id = id;
_name = name;
ReadUpdates();
}
public List<Update> AvailableUpdates { get; set; }
public int Id { get { return _id; } }
public string ProjName
{
get { return _name; }
set { _name = value; }
}
private void ReadUpdates()
{
AvailableUpdates = new List<Update>();
for (int i = 0; i < 10; i++)
{
AvailableUpdates.Add(new
Update(i, DateTime.Now.AddDays(i)));
}
}
}
public class Update
{
private string _title;
private int _uid;
private DateTime _updatedOn;
public Update(int id, DateTime updatedOn)
{
_title = $"Update:{id}";
_uid = id;
_updatedOn = updatedOn;
}
public string Title
{
get { return _title; }
set { _title = value; }
}
public int UId
{
get { return _uid; }
set { _uid = value; }
}
public DateTime UpdatedOn
{
get { return _updatedOn; }
set { _updatedOn = value; }
}
}
public partial class Form1 : Form
{
private Update _currentUpdate;
private Project _project;
public Form1()
{
InitializeComponent();
_project = new Project(1, "Sample Project");
DoBindings();
}
private void DoBindings()
{
NameBox.DataBindings.Add("Text", _project, "ProjName");
IdBox.DataBindings.Add("Text", _project, "Id");
UpdatesCombo.DataSource = _project.AvailableUpdates;
UpdatesCombo.DisplayMember = "UId";
_currentUpdate = (Update)UpdatesCombo.SelectedItem;
UpdateTitle.DataBindings.Add("Text", _currentUpdate, "Title");
UpdateDate.DataBindings.Add("Value", _currentUpdate, "UpdatedOn");
}
private void UpdatesCombo_SelectionChangeCommitted(object sender, System.EventArgs e)
{
_currentUpdate = (Update)UpdatesCombo.SelectedItem;
}
}
Please correct me if I am wrong.
It's quite simple. All you need is to bind the related controls to the same list data source as the combo box.
var updates = _project.AvailableUpdates;
UpdatesCombo.DataSource = updates;
UpdatesCombo.DisplayMember = "UId";
UpdateTitle.DataBindings.Add("Text", updates, "Title");
UpdateDate.DataBindings.Add("Value", updates, "UpdatedOn");
The data binging infrastructure creates CurrencyManager class per each unique list data source. The non list controls are actually bound to the Current property, which is updated by the combo box selection.
I've seen a bunch of posts regarding databinding to databases but none of them have helped with databinding to an existing object in memory. I've also looked at several stack overflow posts where people have said the following code should've bound the properties on my combo box:
projectData = new ProjectData();
this.parentTypeComboBox.DataSource = projectData.MobList;
this.parentTypeComboBox.DisplayMember = "MobType";
this.parentTypeComboBox.ValueMember = "MobType";
My data object has public getters/setters for it's various properties and I've added the INotifyPropertyChanged interface on the classes but do not attach any listeners to the event as of now. From what I've read this should've been all I had to do to get the control to bind to my data object. Any idea why I'm not seeing my combo box get populated with data when my object list changes?
Project data class:
public class ProjectData : INotifyPropertyChanged
{
public static string PROJECT_OUTPUT_DIRECTORY = "..\\";
private List<Mob> _mobList;
public List<Mob> MobList
{
get { return _mobList; }
set { _mobList = value; OnPropertyChanged("MobList"); }
}
public ProjectData()
{
MobList = new List<Mob>();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Mob Class:
//Snippet mob of class
public partial class Mob : IEquatable<Mob>, INotifyPropertyChanged
{
public Mob()
{
dataAttributeField = new List<MobDataAttribute>();
}
private List<MobDataAttribute> dataAttributeField;
private string mobTypeField;
private string parentTypeField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("DataAttribute")]
public List<MobDataAttribute> DataAttribute
{
get
{
return this.dataAttributeField;
}
set
{
this.dataAttributeField = value;
OnPropertyChanged("DataAttribute");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string MobType
{
get
{
return this.mobTypeField;
}
set
{
this.mobTypeField = value;
OnPropertyChanged("MobType");
}
}
}
Using this projectData = new ProjectData(); the MobList is an empty list yet.
If you didn't populate data, you should populate your data to list to show it in ComboBox.
Remember that every time you populate data, you should update DataSource property of your ComboBox:
this.comboBox1.DataSource = parent.Childs;
If you are bound to a data source that does not implement the
IBindingList interface, such as an ArrayList, the bound control's data
will not be updated when the data source is updated. For example, if
you have a combo box bound to an ArrayList and data is added to the
ArrayList, these new items will not appear in the combo box.
Here is a sample:
public partial class SampleForm : Form
{
public SampleForm()
{
InitializeComponent();
}
private void SampleForm_Load(object sender, EventArgs e)
{
//Initialize parent and populate its Childs
var parent = new Parent()
{
ParentName = "Parent 1",
Childs = new List<Child>{
new Child(){ChildName= "Child1"},
new Child(){ChildName= "Child2"}
}
};
this.comboBox1.DataSource = parent.Childs;
this.comboBox1.DisplayMember = "ChildName";
this.comboBox1.ValueMember = "ChildName";
}
}
public class Parent
{
public Parent()
{
Childs = new List<Child>();
}
public string ParentName { get; set; }
public List<Child> Childs { get; set; }
}
public class Child
{
public string ChildName { get; set; }
}
Screenshot:
I'm using MVVM pattern in my project.
My class design is like this:
Class Model : AbstractModel
{
InnerClass Iclass = new InnerClass();
Public String ModelProp1
{
get
{
return Iclass.prop1;
}
set
{
Iclass.prop1 = value;
}
}
public override void SetLabel(UInt16 value, int Index)
{
byte[] arr = BitConverter.GetBytes(value);
this.Iclass.IclassConfig[Index].Label = arr[0];
}
public override string DateFormat
{
get { return Iclass.intlDate.ToString(); }
set { Iclass.intlDate = Convert.ToByte(value); }
}
}
Class InnerClass
{
public byte intlDate
{
get { return this.intl_date; }
set { this.intl_date = value;
RaiseModelPropertiesChangedEvent(new ValueChangedEventArgs { Parameter_dateformat = this.intlDate });
}
private JClassa []channel_config = new JClass[2];
public JClass[] IclassConfig
{
get { return this.channel_config; }
set { this.channel_config = value; }
}
}
Public JClass
{
private byte channel_label;
public byte Label
{
get { return this.channel_label; }
set { this.channel_label = value;}
}
I'm getting data from other application. updated data is coming in InnerClass property from there I want to push this updated data to Model class.
Problem is coming for JClass property how can I fire event such that It will push updated data to model class.
For this I have created Event in InnerClass like this:
public event EventHandler<ValueChangedEventArgs> ModelPropertiesChanged;
public void RaiseModelPropertiesChangedEvent(ValueChangedEventArgs e)
{
if (ModelPropertiesChanged != null)
ModelPropertiesChanged(this, e);
}
public class ValueChangedEventArgs : EventArgs
{
public int Parameter_dateformat { get; set; }
public int Parameter_channelLabel { get; set; }
}
Tell me how Can I achieve this. Becuase I have 4 property in Jclass and 6 Property is InnerClass.
I would add event triggers in the setter of your inner class properties. Then in the Constructor of your parent class, move the IClass = new InnerClass() into your constructor and attach your event listeners.
Since you're MVVM you could leverage INotifyPropertyChanged, but heat will get messy in the long run.
Better to have a 'PropertyName'Changed event for each property you want to notify to the parent class.
So I'm trying to build a small cookbook application using WPF and MVVM light. I've run into a situation where I'm binding a List from the model to the view model. And it works fine for displaying and removing items, but when adding items I couldn't get the display to update.
I came across ObserableCollections which seemed to be just what I wanted, but I'm not sure I'm using them correctly because it seems wrong to be creating a new OC every time. How am I supposed to be retrieving an observable collection when the model is using a list?
Model(s):
public class Recipe
{
public int Id { get; set; }
public string Title { get; set; }
public List<RecipeIngredient> Ingredients { get; set; }
}
public class RecipeIngredient
{
// ... //
}
ViewModel:
public Recipe SelectedRecipe
{
get
{
return this.selectedRecipe;
}
set
{
this.selectedRecipe = value;
RaisePropertyChanged("SelectedRecipe");
RaisePropertyChanged("RecipeIngredients");
}
}
public ObservableCollection<RecipeIngredient> RecipeIngredients
{
get
{
return new ObservableCollection<RecipeIngredient>(selectedRecipe.Ingredients.ToList());
}
}
public RelayCommand<EventArgs> AddIngredientCommand { get; private set; }
public RelayCommand<string> DeleteIngredientCommand { get; private set; }
private void AddIngredient(EventArgs eventArgs)
{
SelectedRecipe.Ingredients.Add(new RecipeIngredient() { Name = "New Ingredient" });
RaisePropertyChanged("RecipeIngredients");
}
private void DeleteIngredient(string name)
{
SelectedRecipe.Ingredients = SelectedRecipe.Ingredients.Where(i => i.Name != name).ToList();
RaisePropertyChanged("RecipeIngredients");
}
public MainViewModel()
{
DBController db = new DBController();
recipes = db.GetRecipeList();
RecipeSelectionChangedCommand = new RelayCommand<SelectionChangedEventArgs>((args) => RecipeSelectionChanged(args));
SaveRecipeCommand = new RelayCommand<EventArgs>((args) => SaveRecipe(args));
AddIngredientCommand = new RelayCommand<EventArgs>((args) => AddIngredient(args));
DeleteIngredientCommand = new RelayCommand<string>((args) => DeleteIngredient(args));
}
Am I way off track here?
Should have read more carefully. If you're displaying the selected recipe's ingredients in an alternate view, you should be using data binding in the view <ListBox ItemsSource="{Binding SelectedRecipe.Ingredients}"/> You could consider using linq to entities (Entity Framework) for ORM..
public class RecipeVM
{
public RecipeVM(Recipe r)
{
recipe = r;
}
Recipe recipe;
public int Id
{
get
{
return recipe.Id;
}
set
{
PropertyChanged("Id");
recipe.id = value;
}
}
public string Title
{
get
{
return recipe.Title;
}
set
{
PropertyChanged("Title");
recipe.Title = value;
}
}
ObservableCollection<RecipeIngredient> ingredients;
public ObservableCollection<RecipeIngredient> Ingredients
{
get
{
if (ingredients == null)
ingredients = new ObservableCollection<RecipeIngredient>(recipe.Ingredients);
return ingredients;
}
set
{
PropertyChanged("Ingredients");
ingredients = value;
}
}
}
You'll need to modify that a bit if you want to keep the collections in sync though..
I'm developing a simple Crud Application (a windows 8.1 store application) using Caliburn Micro 2.0.0-alpha2
I'm in trouble with navigation between viewmodels, passing object.
I read many times the solution proposed by
Anders Gustafsson (How to pass parameter to navigated view model with WinRT Caliburn.Micro?)
and i tried to adapt it to my scope.
But the object is alwais null.
I need to pass a single object selected from a listView to my crudPage.
The crudPage is composed by an userControl that shown the FormView.
So i want to initialize this Form, with the values of the passed object.
I think that the problem is that the "Parameter" is initialized only after the ViewModel is created, but i don't know how to fix that problem.
There is my code, according with the idea of Anders Gustafsson
TransporterListViewModel (a list of Transporters from Database)
public class TransporterListViewModel : ViewModelBase
{
public string Title { get; set; }
public TransporterListViewModel(INavigationService navigationService)
: base(navigationService)
{
LoadData();
}
public async void LoadData() {
_transporters = await TransporterService.GetAll();
}
private BindableCollection<Transporter> _transporters;
public BindableCollection<Transporter> Transporters
{
get
{
return this._transporters;
}
set
{
this._transporters = value;
NotifyOfPropertyChange(() => this.Transporters);
}
}
private Transporter _selectedItem;
public Transporter SelectedItem
{
get
{
return _selectedItem;
}
set
{
_selectedItem = value;
NotifyOfPropertyChange(() => this.SelectedItem);
navigationService.Navigated += NavigationServiceOnNavigated;
navigationService.NavigateToViewModel<TransporterCrudPageViewModel>(_selectedItem;);
navigationService.Navigated -= NavigationServiceOnNavigated;
}
}
private static void NavigationServiceOnNavigated(object sender, NavigationEventArgs args)
{
FrameworkElement view;
TransporterCrudPageViewModel transporterCrudPageViewModel;
if ((view = args.Content as FrameworkElement) == null ||
(transporterCrudPageViewModel = view.DataContext as TransporterCrudPageViewModel) == null) return;
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
}
TransporterCrudViewModel (the page that cointains the UserControl to initialize)
public class TransporterCrudPageViewModel : ViewModelBase
{
public string Title { get; set; }
public Transporter Parameter { get; set; }
public TransporterFormViewModel TransporterFormVM { get; set; }
public async void InitializeTransporterForm(Transporter enumerable)
{
TransporterFormVM = new TransporterFormViewModel(navigationService, enumerable);
await SetUpForm(enumerable);
}
public async Task SetUpForm(Transporter t){
TransporterFormVM.trName = t.trName;
TransporterFormVM.trUrl = t.trUrl;
}
public TransporterCrudPageViewModel(INavigationService navigationService)
: base(navigationService)
{
Title = "TransporterCrud Page";
//this.navigationService = navigationService;
this.InitializeTransporterForm(Parameter);
}
TransporterFormViewModel (the userContol to initialize)
public class TransporterFormViewModel :ViewModelBase
{
public string Title { get; set; }
public Transporter Transporter { get; set; }
public TransporterFormViewModel(INavigationService navigationService,Transporter trans)
: base(navigationService)
{
Transporter = trans;
}
private string _trName;
public string trName
{
get
{
return _trName;
}
set
{
_trName = value;
NotifyOfPropertyChange(() => trName);
}
}
public string trCode { get; set; }
public string trUrl { get; set; }
public int trId { get; set; }
In the constructor TransporterCrudViewModel class you have:
this.InitializeTransporterForm(Parameter);
where Parameter is a property of type Transporter not initialized and you will call the method InitializeTransporterForm with a null parameter. Then you'll call SetUpForm method with a null value of the parameter Transporter t. I think you should initialize in some way this property.
Then, supposing you're continuing in your TransporterListViewModel class with this:
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
in the method InitializeTransporterForm, you don't set the passed parameter as value of the property Parameter with something like this:
public async void InitializeTransporterForm(Transporter enumerable)
{
TransporterFormVM = new TransporterFormViewModel(navigationService, enumerable);
this.Parameter = enumerable; //setting the Parameter property..
await SetUpForm(enumerable);
}
Beside these notes, you should put a breakpoint with your IDE in the line
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
Make sure that the property Parameter of the NavigationEventArgs object is not null.