At the moment I have this Code (Deepth = 2 Categories)
private void Window_Loaded(object sender, RoutedEventArgs e)
{
foreach (Kategorie kategorie in mainViewModel.Kategorien)
{
if (kategorie.Oberkategorie == null)
{
TreeViewItem newChild = new TreeViewItem();
newChild.Header = kategorie.Bezeichnung;
treeView.Items.Add(newChild);
foreach (Kategorie unterkategorie in mainViewModel.Kategorien)
{
if (unterkategorie.Oberkategorie != null)
{
if (unterkategorie.Oberkategorie.Id == kategorie.Id)
{
TreeViewItem subItem = new TreeViewItem();
subItem.Header = unterkategorie.Bezeichnung;
newChild.Items.Add(subItem);
}
}
}
}
}
}
I would like to make a recursive method, where I could have n Categories in undercategories and so on, I tried things like:
foreach (Kategorie kategorie in mainViewModel.Kategorien)
{
FillKategorienTreeView(kategorie);
}
and then:
private void FillKategorienTreeView(Kategorie kategorie)
{
//if (kategorie.Oberkategorie == null)
//{
// TreeViewItem newChild = new TreeViewItem();
// newChild.Header = kategorie.Bezeichnung;
// treeView.Items.Add(newChild);
//}
//else
//{
// if (kategorie.Oberkategorie.Id == kategorie.Id)
// {
// TreeViewItem subItem = new TreeViewItem();
// subItem.Header = kategorie.Bezeichnung;
// newChild.Items.Add(subItem);
// }
//}
}
and other similiar things and it didn't work, I'm not so friendly with recursion but in this case it's indispensable..
There's a Treeview, which has a TopLevel (Maincategory) and can have n under/subcategories.
Hope u can help me
EDIT: The Category Object:
[DataMember]
public int Id
{
get { return _id; }
set
{
if (_id != value)
{
if (ChangeTracker.ChangeTrackingEnabled && ChangeTracker.State != ObjectState.Added)
{
throw new InvalidOperationException("The property 'Id' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state.");
}
_id = value;
OnPropertyChanged("Id");
}
}
}
private int _id;
[DataMember]
public string Bezeichnung
{
get { return _bezeichnung; }
set
{
if (_bezeichnung != value)
{
_bezeichnung = value;
OnPropertyChanged("Bezeichnung");
}
}
}
private string _bezeichnung;
#endregion
#region Navigation Properties
[DataMember]
public TrackableCollection<Artikel> Artikel
{
get
{
if (_artikel == null)
{
_artikel = new TrackableCollection<Artikel>();
_artikel.CollectionChanged += FixupArtikel;
}
return _artikel;
}
set
{
if (!ReferenceEquals(_artikel, value))
{
if (ChangeTracker.ChangeTrackingEnabled)
{
throw new InvalidOperationException("Cannot set the FixupChangeTrackingCollection when ChangeTracking is enabled");
}
if (_artikel != null)
{
_artikel.CollectionChanged -= FixupArtikel;
}
_artikel = value;
if (_artikel != null)
{
_artikel.CollectionChanged += FixupArtikel;
}
OnNavigationPropertyChanged("Artikel");
}
}
}
private TrackableCollection<Artikel> _artikel;
[DataMember]
public TrackableCollection<Kategorie> Unterkategorie
{
get
{
if (_unterkategorie == null)
{
_unterkategorie = new TrackableCollection<Kategorie>();
_unterkategorie.CollectionChanged += FixupUnterkategorie;
}
return _unterkategorie;
}
set
{
if (!ReferenceEquals(_unterkategorie, value))
{
if (ChangeTracker.ChangeTrackingEnabled)
{
throw new InvalidOperationException("Cannot set the FixupChangeTrackingCollection when ChangeTracking is enabled");
}
if (_unterkategorie != null)
{
_unterkategorie.CollectionChanged -= FixupUnterkategorie;
}
_unterkategorie = value;
if (_unterkategorie != null)
{
_unterkategorie.CollectionChanged += FixupUnterkategorie;
}
OnNavigationPropertyChanged("Unterkategorie");
}
}
}
private TrackableCollection<Kategorie> _unterkategorie;
[DataMember]
public Kategorie Oberkategorie
{
get { return _oberkategorie; }
set
{
if (!ReferenceEquals(_oberkategorie, value))
{
var previousValue = _oberkategorie;
_oberkategorie = value;
FixupOberkategorie(previousValue);
OnNavigationPropertyChanged("Oberkategorie");
}
}
}
private Kategorie _oberkategorie;
This will only work if each category has a list of children or sub categories in the object. If not then you will have to build the list of children for each item.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
foreach (Kategorie kategorie in mainViewModel.Kategorien)
{
LoadTreeviewItem(kategorie, null);
}
}
private void LoadTreeviewItem(Kategorie kategorie, TreeViewItem parentItem)
{
//Stop condition
if(kategorie == null) return;
TreeViewItem newChild = new TreeViewItem();
newChild.Header = kategorie.Bezeichnung;
treeView.Items.Add(newChild);
if(parentItem != null) // Add to parent if it is not null
{
parentItem.Items.Add(newChild);
}
else //Otherwise this is the top level so add to treeview
{
treeView.Items.Add(newChild);
}
foreach (Kategorie subkategorie in kategorie.Unterkategorie)
{
LoadTreeviewItem(subkategorie, parentItem);
}
}
Related
I'm pretty new to C#, I'm trying to make an app with Xamarin forms. I want a picker, what can choose from ($,£,etc) values. Keep in mind that I'm using SQLite.net. Soo, for learning how to make an app I'm using an example from github. There is a long code for bindablepicker:
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using Xamarin.Forms;
namespace TodoScheduler.Controls
{
public class BindablePicker : Picker
{
bool _disableNestedCalls;
public static readonly BindableProperty PickerTitleProperty =
BindableProperty.Create("PickerTitle", typeof(string), typeof(BindablePicker),
null);
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(BindablePicker),
null, propertyChanged: OnItemsSourceChanged);
public static readonly BindableProperty SelectedItemProperty =
BindableProperty.Create("SelectedItem", typeof(Object), typeof(BindablePicker),
null, BindingMode.TwoWay, propertyChanged: OnSelectedItemChanged);
public static readonly BindableProperty SelectedValueProperty =
BindableProperty.Create("SelectedValue", typeof(Object), typeof(BindablePicker),
null, BindingMode.TwoWay, propertyChanged: OnSelectedValueChanged);
public String DisplayMemberPath { get; set; }
public string PickerTitle
{
get { return (string)GetValue(PickerTitleProperty); }
set { SetValue(PickerTitleProperty, value); }
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set
{
if (this.SelectedItem != value)
{
SetValue(SelectedItemProperty, value);
InternalSelectedItemChanged();
}
}
}
public object SelectedValue
{
get { return GetValue(SelectedValueProperty); }
set
{
SetValue(SelectedValueProperty, value);
InternalSelectedValueChanged();
}
}
public string SelectedValuePath { get; set; }
public BindablePicker()
{
this.SelectedIndexChanged += OnSelectedIndexChanged;
}
public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
void InstanceOnItemsSourceChanged(Object oldValue, Object newValue)
{
_disableNestedCalls = true;
this.Items.Clear();
var oldCollectionINotifyCollectionChanged = oldValue as INotifyCollectionChanged;
if (oldCollectionINotifyCollectionChanged != null)
{
oldCollectionINotifyCollectionChanged.CollectionChanged -= ItemsSource_CollectionChanged;
}
var newCollectionINotifyCollectionChanged = newValue as INotifyCollectionChanged;
if (newCollectionINotifyCollectionChanged != null)
{
newCollectionINotifyCollectionChanged.CollectionChanged += ItemsSource_CollectionChanged;
}
if (!Equals(newValue, null))
{
var hasDisplayMemberPath = !String.IsNullOrWhiteSpace(this.DisplayMemberPath);
foreach (var item in (IEnumerable)newValue)
{
if (hasDisplayMemberPath)
{
var type = item.GetType();
var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
this.Items.Add(prop.GetValue(item).ToString());
}
else
{
this.Items.Add(item.ToString());
}
}
this.SelectedIndex = -1; // select first item by default
this._disableNestedCalls = false;
if (this.SelectedItem != null)
{
this.InternalSelectedItemChanged();
}
else if (hasDisplayMemberPath && this.SelectedValue != null)
{
this.InternalSelectedValueChanged();
}
}
else
{
_disableNestedCalls = true;
this.SelectedIndex = -1;
this.SelectedItem = null;
this.SelectedValue = null;
_disableNestedCalls = false;
}
}
void InternalSelectedItemChanged()
{
if (_disableNestedCalls)
{
return;
}
var selectedIndex = -1;
Object selectedValue = null;
if (this.ItemsSource != null)
{
var index = 0;
var hasSelectedValuePath = !String.IsNullOrWhiteSpace(this.SelectedValuePath);
foreach (var item in ItemsSource)
{
if (item != null && item.Equals(this.SelectedItem))
{
selectedIndex = index;
if (hasSelectedValuePath)
{
var type = item.GetType();
var prop = type.GetRuntimeProperty(this.SelectedValuePath);
selectedValue = prop.GetValue(item);
}
break;
}
index++;
}
}
_disableNestedCalls = true;
this.SelectedValue = selectedValue;
this.SelectedIndex = selectedIndex;
_disableNestedCalls = false;
}
void InternalSelectedValueChanged()
{
if (_disableNestedCalls)
{
return;
}
if (String.IsNullOrWhiteSpace(this.SelectedValuePath))
{
return;
}
var selectedIndex = -1;
Object selectedItem = null;
var hasSelectedValuePath = !String.IsNullOrWhiteSpace(this.SelectedValuePath);
if (this.ItemsSource != null && hasSelectedValuePath)
{
var index = 0;
foreach (var item in ItemsSource)
{
if (item != null)
{
var type = item.GetType();
var prop = type.GetRuntimeProperty(this.SelectedValuePath);
if (Object.Equals(prop.GetValue(item), this.SelectedValue))
{
selectedIndex = index;
selectedItem = item;
break;
}
}
index++;
}
}
_disableNestedCalls = true;
this.SelectedItem = selectedItem;
this.SelectedIndex = selectedIndex;
_disableNestedCalls = false;
}
void ItemsSource_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
{
var hasDisplayMemberPath = !String.IsNullOrWhiteSpace(this.DisplayMemberPath);
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
if (hasDisplayMemberPath)
{
var type = item.GetType();
var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
this.Items.Add(prop.GetValue(item).ToString());
}
else
{
this.Items.Add(item.ToString());
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var item in e.NewItems)
{
if (hasDisplayMemberPath)
{
var type = item.GetType();
var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
this.Items.Remove(prop.GetValue(item).ToString());
}
else
{
this.Items.Remove(item.ToString());
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (var item in e.NewItems)
{
if (hasDisplayMemberPath)
{
var type = item.GetType();
var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
this.Items.Remove(prop.GetValue(item).ToString());
}
else
{
var index = this.Items.IndexOf(item.ToString());
if (index > -1)
{
this.Items[index] = item.ToString();
}
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
this.Items.Clear();
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
if (hasDisplayMemberPath)
{
var type = item.GetType();
var prop = type.GetRuntimeProperty(this.DisplayMemberPath);
this.Items.Remove(prop.GetValue(item).ToString());
}
else
{
var index = this.Items.IndexOf(item.ToString());
if (index > -1)
{
this.Items[index] = item.ToString();
}
}
}
}
else
{
_disableNestedCalls = true;
this.SelectedItem = null;
this.SelectedIndex = -1;
this.SelectedValue = null;
_disableNestedCalls = false;
}
}
}
static void OnItemsSourceChanged(BindableObject bindable, Object oldValue, Object newValue)
{
if (Equals(newValue, null) && Equals(oldValue, null))
{
return;
}
var picker = (BindablePicker)bindable;
picker.InstanceOnItemsSourceChanged(oldValue, newValue);
}
void OnSelectedIndexChanged(Object sender, EventArgs e)
{
if (_disableNestedCalls)
{
return;
}
if (this.SelectedIndex < 0 || this.ItemsSource == null || !this.ItemsSource.GetEnumerator().MoveNext())
{
_disableNestedCalls = true;
if (this.SelectedIndex != -1)
{
//this.SelectedIndex = -1;
this.SelectedIndex = 0;
}
this.SelectedItem = null;
this.SelectedValue = null;
_disableNestedCalls = false;
return;
}
_disableNestedCalls = true;
var index = 0;
var hasSelectedValuePath = !String.IsNullOrWhiteSpace(this.SelectedValuePath);
foreach (var item in ItemsSource)
{
if (index == this.SelectedIndex)
{
this.SelectedItem = item;
if (hasSelectedValuePath)
{
var type = item.GetType();
var prop = type.GetRuntimeProperty(this.SelectedValuePath);
this.SelectedValue = prop.GetValue(item);
}
break;
}
index++;
}
_disableNestedCalls = false;
}
static void OnSelectedItemChanged(BindableObject bindable, Object oldValue, Object newValue)
{
var boundPicker = (BindablePicker)bindable;
boundPicker.ItemSelected?.Invoke(boundPicker, new SelectedItemChangedEventArgs(newValue));
boundPicker.InternalSelectedItemChanged();
}
static void OnSelectedValueChanged(BindableObject bindable, Object oldValue, Object newValue)
{
var boundPicker = (BindablePicker)bindable;
boundPicker.InternalSelectedValueChanged();
}
}
}
The form code:
<controls:BindablePicker Margin="20,5"
ItemsSource="{Binding PriorityList}"
SelectedItem="{Binding Priority, Mode=TwoWay}"
DisplayMemberPath="Title"
Title="Choose"
VerticalOptions="EndAndExpand"/>
Model:
TodoPriority _priority = TodoPriority.Low;
[NotNull] public TodoPriority Priority {
get { return _priority; }
set { SetProperty(ref _priority, value); }
}
ViewModel:
TodoPriority _priority = TodoPriority.Low;
public TodoPriority Priority
{
get { return _priority; }
set { SetProperty(ref _priority, value); }
}
IEnumerable<TodoPriority> _priorityList = Enum.GetValues(typeof(TodoPriority)).Cast<TodoPriority>();
public IEnumerable<TodoPriority> PriorityList
{
get { return _priorityList; }
set { SetProperty(ref _priorityList, value); }
}
TodoPriority:
namespace TodoScheduler.Enums
{
public enum TodoPriority
{
Low = 0,
Normal,
High
}
}
At the moment I can choose from picker (low,normal,high), but how can I change this? I know I cant change those values from Todopriority to currency token, because its enum. I tried to make them all strings, but then I caught on a problem that how I can make these values. I know its a bit dumb question, but I've search alot and cant figute it out.
You could try to create a class to represent each picker item
class PriorityPickerValue {
public string Title { get; set; }
public TodoPriority Priority { get; set; }
}
That way you can specify how your values look in the picker and still have access to the TodoPriority property
I've a WPF application that loads a menu from an XML file, each node as a tag that identifies the user function. Each user has visibility permission that match against the tag defined in the xml file. I wish some help on simplifing that code since I's quite complex and from my point of view poor performing. Consider that the main menu is composed of main items and inside each there're specific areas function. If a user is enabled to at element at list the main menu node is shown otherwise not.
public virtual System.Threading.Tasks.Task<MenuItemNode> RegisterMenu(IDictionary<string,Type> functions)
{
var assembly = Assembly.GetCallingAssembly(); //I should get the module that invoked the base class
string filename = GetFullFileName(assembly, MenuFilename);
return Task.Factory.StartNew<MenuItemNode>(() =>
{
string xmlFileName = string.Format(filename);
var doc = new XmlDocument();
using (Stream stream = assembly.GetManifestResourceStream(xmlFileName))
{
if (stream != null)
{
using (var reader = new StreamReader(stream))
{
doc.LoadXml(reader.ReadToEnd());
}
}
}
MenuItemNode menu = BuildMenu(doc.SelectSingleNode(#"/Node"), "/", functions);
return menu;
});
}
private string GetFullFileName(Assembly assembly,string filename)
{
var resourceFiles = assembly.GetManifestResourceNames();
return resourceFiles.First(x => x.EndsWith(filename));
}
private MenuItemNode BuildMenu(XmlNode parent, string path, IDictionary<string, Type> functions)
{
Argument.IsNotNull(() => parent);
if (functions == null || (functions.Count == 0)) return null;
MenuItemNode menuItem = null;
string subPath = "Node";
string name = string.Empty;
string tag = string.Empty;
int position = 0;
bool forceVisible = false;
string parameters = string.Empty;
string group = string.Empty;
bool showInDialog = false;
if (parent.Attributes != null)
{
if (parent.Attributes["name"] != null)
name = parent.Attributes["name"].Value;
if (parent.Attributes["tag"] != null)
tag = parent.Attributes["tag"].Value;
if (parent.Attributes["position"] != null)
position = System.Convert.ToInt32(parent.Attributes["position"].Value);
if (parent.Attributes["force_visible"] != null)
forceVisible = Convert.ToBoolean(parent.Attributes["force_visible"].Value);
if (parent.Attributes["parameters"] != null)
parameters = parent.Attributes["parameters"].Value;
if (parent.Attributes["group"] != null)
group = parent.Attributes["group"].Value;
if (parent.Attributes["showindialog"] != null)
showInDialog = Convert.ToBoolean(parent.Attributes["showindialog"].Value);
}
//parent item
if (string.IsNullOrEmpty(tag))
{
menuItem = CreateMenuItem(name, position);
menuItem.ForceVisible = forceVisible;
// menuItem.Group = group;
}
else//child item
{
if (functions.ContainsKey(tag))
{
menuItem = CreateMenuItem(name, tag, position);
menuItem.ForceVisible = forceVisible;
//menuItem.GroupName = group;
menuItem.ShowInDialog = showInDialog;
//menuItem.MenuParameter = GetMenuItemParameters(parameters);
#region Multiple-tag
if ((functions == null) || !functions.Any()) return null;
#endregion
}
else
{
//todo: add-logging
}
}
if (parent.HasChildNodes)
{
foreach (XmlNode child in parent.SelectNodes(subPath))
{
MenuItemNode childMenuItem = BuildMenu(child, subPath, functions);
if (childMenuItem == null) continue;
int childPosition = childMenuItem.SortIndex;
//This to prevent out-of-boundaries exception
if (childPosition > menuItem.Children.Count)
childPosition = menuItem.Children.Count;
menuItem.Children.Insert(childPosition, childMenuItem);
}
}
return menuItem;
}
private MenuItemNode CreateMenuItem(string text, int position)
{
var item = new MenuItemNode();
item.Text = text;
item.SortIndex = position;
return item;
}
private MenuItemNode CreateMenuItem(string text, string tag, int? position)
{
MenuItemNode item = CreateMenuItem(text, (!position.HasValue) ? 0 : position.Value);
item.FunctionTag = tag;
item.SortIndex = (!position.HasValue) ? 0 : position.Value;
return item;
}
And here's the MenuItemNode class
[ContentProperty("Children")]
public class MenuItemNode : INotifyPropertyChanged
{
private string text;
private ICommand command;
private Uri imageSource;
private int sortIndex;
private bool forceVisible;
private bool showInDialog;
private bool isChecked;
public bool IsChecked
{
get
{
return this.isChecked;
}
set
{
if (this.isChecked != value)
{
this.isChecked = value;
this.RaisePropertyChanged(() => this.IsChecked);
}
}
}
public bool IsSeparator { get; set; }
public MenuItemNode()
{
Children = new MenuItemNodeCollection();
SortIndex = 50;
SetCommand();
}
public MenuItemNode(String path)
: base()
{
Path = path;
}
public MenuItemNodeCollection Children { get; private set; }
public virtual ICommand Command
{
get
{
return command;
}
set
{
if (command != value)
{
command = value;
RaisePropertyChanged(() => this.Command);
}
}
}
public Uri ImageSource
{
get
{
return imageSource;
}
set
{
if (imageSource != value)
{
imageSource = value;
RaisePropertyChanged(() => this.ImageSource);
}
}
}
public string Text
{
get
{
return text;
}
set
{
if (text != value)
{
text = value;
RaisePropertyChanged(() => this.Text);
}
}
}
private MenuGroupDescription group;
public MenuGroupDescription Group
{
get { return group; }
set
{
if (group != value)
{
group = value;
RaisePropertyChanged(() => this.Group);
}
}
}
public int SortIndex
{
get
{
return sortIndex;
}
set
{
if (sortIndex != value)
{
sortIndex = value;
RaisePropertyChanged(() => this.SortIndex);
}
}
}
public string Path
{
get;
private set;
}
public bool ForceVisible
{
get
{
return this.forceVisible;
}
set
{
if (forceVisible != value)
{
this.forceVisible = value;
RaisePropertyChanged(() => ForceVisible);
}
}
}
public bool ShowInDialog
{
get
{
return this.showInDialog;
}
set
{
if (showInDialog = value)
{
this.showInDialog = value;
RaisePropertyChanged(() => ShowInDialog);
}
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyExpression.Name));
}
#endregion
protected virtual void SetCommand()
{
this.Command = FunctionMenuCommands.OpenFunctionCommand;
}
public string FunctionTag { get; set; }
}
In specific what I did is to process each child node then if visible I add it to the collection... do you see any possible better solution?
Thanks
Whoever wrote that code clearly didn't know how to write WPF. In WPF, there is a much simpler option... just use the MenuItem.Visibility property to hide (collapse) MenuItems that users don't have access to. Of course, you'd need some data bind-able security bool properties which you could then data bind to the MenuItem.Visibility property of each MenuItem via a BooleanToVisibilityConverter. Here's a simple example:
<MenuItem Header="Some option" Visibility="{Binding User.Security.HasSomeOptionPermission,
Converter={StaticResource BooleanToVisibilityConverter}}" ... />
If a particular user has the SomeOptionPermission, then the MenuItem will be displayed, otherwise it will be hidden.
I need a way of dynamically creating a number of text boxes and accessing their values.
In my form the user enters a number between 1 and 50 and then that number of text boxes must be created with dynamic names ie ingredient1, ingredient2, ingredient3, ...ingredient50 etc.
I have a for loop that will create a number of text boxes using the value, but how do I store the text box value in a string variable?
Here is the for loop currently empty
int i = Form1.ingredientCount;
for (i = 1; i < Form1.ingredientCount; i++)
{
//create new text box
//create new string that then holds value from text box
}
For clarification:
A user enters a number on the previous page.
This number then determines the number of textboxes created and the number of strings created.
The textboxes and strings need to have uniquely generated IDs in a for loop.
I also need another textbox for the weight of each ingredient although I could figure that out myself.
So basically I want each text box and string to be named something like
"input" + i (where i is incrementer)
so that would make the name be "input1", "input2", "input3" and so on.
Same with the string which will contain data from textbox.
I thought i would edit it, as it seems i had misunderstood the question
you could bind it in the form as the following:
public partial class Form1 : Form
{
Recepie pancakes = new Recepie();
IList<UniqueHolder> items = new List<UniqueHolder>();
public Form1()
{
InitializeComponent();
pancakes.Ingredients.Add(new Ingredient { Title = "Milk - 250 gr" });
pancakes.Ingredients.Add(new Ingredient { Title = "Butter - 25 gr" });
pancakes.Ingredients.Add(new Ingredient { Title = "Oil - 1 large spoon" });
pancakes.Ingredients.Add(new Ingredient { Title = "Sugar - 100 gr" });
pancakes.Ingredients.Add(new Ingredient { Title = "Flower - 200 gr" });
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
for (var i = 0; i < pancakes.Ingredients.Count; i++)
{
Ingredient ing = pancakes.Ingredients[i];
TextBox tb = new TextBox { Location = new Point(10, i * 30), Size = new Size(200, 20), Text = ing.Title };
UniqueHolder uh = new UniqueHolder { Ingredient = ing, TextBox = tb };
this.Controls.Add(tb);
}
}
}
The unique holder does the databinding on changes in either ingredient or textbox
public class UniqueHolder : IDisposable
{
public Guid UniqueID { get; set; }
public override bool Equals(object obj)
{
if (obj is UniqueHolder)
{
return Guid.Equals(((UniqueHolder)obj).UniqueID, this.UniqueID);
}
return base.Equals(obj);
}
public override int GetHashCode()
{
return UniqueID.GetHashCode();
}
private TextBox textbox;
public TextBox TextBox
{
get
{
return textbox;
}
set
{
if (object.Equals(textbox, value))
{
return;
}
if (textbox != null)
{
textbox.TextChanged -= OnTextChanged;
}
textbox = value;
if (textbox != null)
{
textbox.TextChanged += OnTextChanged;
}
}
}
private Ingredient ingredient;
public Ingredient Ingredient
{
get
{
return ingredient;
}
set
{
if (object.Equals(ingredient, value))
{
return;
}
if (ingredient != null)
{
ingredient.PropertyChanged -= OnIngredientChanged;
}
ingredient = value;
if (ingredient != null)
{
ingredient.PropertyChanged += OnIngredientChanged;
}
}
}
public UniqueHolder()
{
this.UniqueID = Guid.NewGuid();
}
protected virtual void OnIngredientChanged(object sender, PropertyChangedEventArgs e)
{
if (string.Equals(e.PropertyName, "Title", StringComparison.OrdinalIgnoreCase))
{
if (TextBox == null)
{
return;
}
TextBox.Text = Ingredient.Title;
}
}
protected virtual void OnTextChanged(object sender, EventArgs e)
{
var tb = sender as TextBox;
if (tb == null)
{
return;
}
if (Ingredient == null)
{
return;
}
Ingredient.Title = tb.Text;
}
public void Dispose()
{
Ingredient = null;
TextBox = null;
}
}
And you can get back to the recepie using the ingredients as such
public class Recepie : IDisposable
{
private readonly IList<Ingredient> ingredients = new ObservableCollection<Ingredient>();
public IList<Ingredient> Ingredients
{
get
{
return ingredients;
}
}
protected virtual void OnIngredientsListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
var ing = item as Ingredient;
if (ing == null)
{
continue;
}
ing.Recepie = null;
}
}
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
var ing = item as Ingredient;
if (ing == null)
{
continue;
}
ing.Recepie = this;
}
}
}
public Recepie()
{
var obs = Ingredients as INotifyCollectionChanged;
if (obs != null)
{
obs.CollectionChanged += OnIngredientsListChanged;
}
}
public void Dispose()
{
int total = Ingredients.Count;
for (int i = total; --i >= 0; )
{
Ingredients.RemoveAt(i);
}
var obs = Ingredients as INotifyCollectionChanged;
if (obs != null)
{
obs.CollectionChanged -= OnIngredientsListChanged;
}
}
}
public class Ingredient : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Recepie recepie;
public virtual Recepie Recepie
{
get
{
return recepie;
}
set
{
if (object.Equals(recepie, value))
{
return;
}
recepie = value;
RaisePropertyChanged("Recepie");
}
}
private string title;
public string Title
{
get
{
return title;
}
set
{
if (string.Equals(title, value))
{
return;
}
title = value;
RaisePropertyChanged("Title");
}
}
protected virtual void RaisePropertyChanged(string propertyName)
{
var local = PropertyChanged;
if (local != null)
{
local.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Rather than creating a string to hold the value from the textboxes, keep the textboxes in a list and get the value out from the Text property when you need it: then you don't need to wire up the TextChanged event etc. Edit: you don't need a string variable to store the textbox text, as you now have a reference to that via the list and the Text property when it's needed.
// a field
List<TextBox> ingredientTextBoxes = new List<TextBox>();
private void Populate(int ingredientCount)
{
for (i = 1; i < ingredientCount; i++)
{
// Assign a consecutive name so can order by it
TextBox tb = new TextBox { Name = "IngredientTextbox" + i};
ingredientTextBoxes.Add(tb);
}
}
// when you want the ingredients
public List<string> GetIngredients
{
List<string> ingredients = new List<string>();
foreach (var tb in ingredientTextBoxes)
{
ingredients.Add(tb.text);
}
return ingredients;
}
// contains
private List<string> GetIngredientsMatching(string match)
{
// Contains really should take a StringComparison parameter, but it doesn't
return ingredientTextBoxes
.Where(t => t.Text.ToUpper().Contains(match.ToUpper()))
.Select(t => t.Text);
}
Edit: if you're going to have multiple values per ingredient then use a User Control which exposes the properties you want - summarising amount, weight, description (e.g. "a pinch" of salt) - in the most convenient way for the code accessing these properties.
I populate a data grid with a list of objects that come from a repository like this:
public static IEnumerable<Order> GetOrdersForDataGrid()
{
IEnumerable<Order> query;
using (RSDContext = new RSDContext())
{
query = context.Orders.Include(o=>o.OrderDetails).ToList();
}
return query;
}
When I want to edit an order I pass the selected row to a new window like this:
OrderEditWindow orderEdit = new OrderEditWindow();
orderEdit.SelectedOrder = SelectedOrder;
orderEdit.ShowDialog();
Here I set the DataContext of the Window to:
DataContext = SelectedOrder;
In this window I have another data grid that binds to OrderDetails collection property of Order. The problem is on CRUD operations on OrderDetails. For example, after I add a new orderDetail like this:
private void AddProductDetailButton_OnClick(object sender, RoutedEventArgs e)
{
if (!ValidateProductDetail())
return;
var _selectedProduct = ProductAutoCompleteBox.SelectedItem as Product;
var selectedProduct = ProductsRepository.GetProductById(_selectedProduct.ProductId);
OrderDetail orderDetail = new OrderDetail();
orderDetail.Price = selectedProduct.Price;
orderDetail.ProductCode = selectedProduct.Code;
orderDetail.ProductName = selectedProduct.Name;
orderDetail.Quantity = int.Parse(QuantityNumericUpDown.Value.ToString());
orderDetail.Um = selectedProduct.Um;
orderDetail.Total = selectedProduct.Price * int.Parse(QuantityNumericUpDown.Value.ToString());
orderDetail.Group = selectedProduct.Subgroup.Group.Name;
orderDetail.Subgroup = selectedProduct.Subgroup.Name;
orderDetail.SupplierName = selectedProduct.Supplier.Name;
//orderDetail.Order=SelectedOrder;
//orderDetail.OrderId = SelectedOrder.OrderId;
SelectedOrder.OrderDetails.Add(orderDetail);
ProductAutoCompleteBox.Text = string.Empty;
QuantityNumericUpDown.Value = 1;
ProductAutoCompleteBox.Focus();
}
and then I call the update method from repository:
public static void UpdateOrder(Order order)
{
using (RSDContext context = new RSDContext())
{
context.Orders.Attach(order);
context.Entry(order).State = EntityState.Modified;
context.SaveChanges();
}
}
I get an error about OrderId. If i set manualy the navigation property and the id I don't get an error but changes dont get saved into db.
My Order model look like this:
public class Order : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Order()
{
_OrderDetails = new ObservableCollection<OrderDetail>();
_OrderDetails.CollectionChanged += _OrderDetails_CollectionChanged;
}
void _OrderDetails_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
AttachProductChangedEventHandler(e.NewItems.Cast<OrderDetail>());
if (e.OldItems != null)
CalcualteTotals();
}
[NotMapped]
public decimal CalculatedTotal
{
get
{
return OrderDetails.Sum(x => x.Total);
}
}
public int OrderId { get; set; }
private int _Number;
public int Number
{
get { return _Number; }
set
{
_Number = value;
NotifyPropertyChanged("Number");
}
}
private DateTime _Date;
public DateTime Date
{
get { return _Date; }
set
{
_Date = value;
NotifyPropertyChanged("Date");
}
}
private bool _Canceled;
public bool Canceled
{
get { return _Canceled; }
set
{
_Canceled = value;
NotifyPropertyChanged("Canceled");
}
}
private string _ClientName;
public string ClientName
{
get { return _ClientName; }
set
{
_ClientName = value;
NotifyPropertyChanged("ClientName");
}
}
private string _ClientPhone;
public string ClientPhone
{
get { return _ClientPhone; }
set
{
_ClientPhone = value;
NotifyPropertyChanged("ClientPhone");
}
}
private string _DeliveryAddress;
public string DeliveryAddress
{
get { return _DeliveryAddress; }
set
{
_DeliveryAddress = value;
NotifyPropertyChanged("DeliveryAddress");
}
}
private decimal _Transport;
public decimal Transport
{
get { return _Transport; }
set
{
_Transport = value;
NotifyPropertyChanged("Transport");
}
}
private decimal _Total;
public decimal Total
{
get { return _Total; }
set
{
_Total = value;
NotifyPropertyChanged("Total");
}
}
private ObservableCollection<OrderDetail> _OrderDetails;
public virtual ObservableCollection<OrderDetail> OrderDetails
{
//get { return _OrderDetails ?? (_OrderDetails = new ObservableCollection<OrderDetail>()); }
get
{
return _OrderDetails;
}
set
{
_OrderDetails = value;
NotifyPropertyChanged("OrderDetails");
}
}
private void AttachProductChangedEventHandler(IEnumerable<OrderDetail> orderDetails)
{
foreach (var p in orderDetails)
{
p.PropertyChanged += (sender, e) =>
{
switch (e.PropertyName)
{
case "Quantity":
case "Price":
case "Total":
CalcualteTotals();
break;
}
};
}
CalcualteTotals();
}
public void CalcualteTotals()
{
NotifyPropertyChanged("CalculatedTotal");
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
And my OrderDetail model look like this:
public class OrderDetail : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int OrderDetailId { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
private int _ProductCode;
public int ProductCode
{
get { return _ProductCode; }
set
{
_ProductCode = value;
NotifyPropertyChanged("ProductCode");
}
}
private string _ProductName;
public string ProductName
{
get { return _ProductName; }
set
{
_ProductName = value;
NotifyPropertyChanged("ProductName");
}
}
private string _Um;
public string Um
{
get { return _Um; }
set
{
_Um = value;
NotifyPropertyChanged("Um");
}
}
private decimal _Price;
public decimal Price
{
get { return _Price; }
set
{
_Price = value;
NotifyPropertyChanged("Price");
NotifyPropertyChanged("Total");
}
}
private int _Quantity;
public int Quantity
{
get { return _Quantity; }
set
{
_Quantity = value;
NotifyPropertyChanged("Quantity");
NotifyPropertyChanged("Total");
}
}
private string _SupplierName;
public string SupplierName
{
get { return _SupplierName; }
set
{
_SupplierName = value;
NotifyPropertyChanged("SupplierName");
}
}
private string _Subgroup;
public string Subgroup
{
get { return _Subgroup; }
set
{
_Subgroup = value;
NotifyPropertyChanged("Subgroup");
}
}
private string _Group;
public string Group
{
get { return _Group; }
set
{
_Group = value;
NotifyPropertyChanged("Group");
}
}
public decimal _Total;
public decimal Total
{
get { return Quantity * Price; }
set
{
_Total = value;
NotifyPropertyChanged("Total");
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
I'm really trying to use some sort of unit of work and I don't understand how i'm supposed to apply CRUD on objects with child collections and keep the UI updated in the same time (by working in a ObservableCollection and using Binding ClientPhone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged my parent window is updated as I type)
A final working solution:
using (RSDContext context = new RSDContext())
{
var details = order.OrderDetails;
order.OrderDetails = null;
List<int> OriginalOrderDetailsIds =
context.OrderDetails.Where(o => o.OrderId == order.OrderId).Select(o => o.OrderDetailId).ToList();
List<int> CurrentOrderDetailsIds = details.Select(o => o.OrderDetailId).ToList();
List<int> DeletedOrderDetailsIds = OriginalOrderDetailsIds.Except(CurrentOrderDetailsIds).ToList();
context.Entry(order).State = EntityState.Modified;
foreach (var deletedOrderDetailId in DeletedOrderDetailsIds)
{
context.Entry(context.OrderDetails.Single(o => o.OrderDetailId == deletedOrderDetailId)).State = EntityState.Deleted;
}
foreach (OrderDetail detail in details)
{
// Add.
if (detail.OrderDetailId == 0)
{
detail.OrderId = order.OrderId;
context.Entry(detail).State = EntityState.Added;
}
// Update.
else
{
context.Entry(detail).State = EntityState.Modified;
}
}
context.SaveChanges();
}
You could do this way for adding and updating the child, but not sure about deleted order details in the ui. If you don't want to get the order from entity, you need some kind of marking in the OrderDetail for deleted OrderDetail.
using (RSDContext context = new RSDContext())
{
var details = order.OrderDetails;
order.OrderDetails = null;
context.Entry(order).State = EntityState.Modified;
foreach (var detail in details)
{
if (detail.Id == 0)
{
// Adds.
detail.OrderId = order.Id;
context.Entry(detail).State = EntityState.Added;
}
else if (detail.IsDeleted)
// Adds new property called 'IsDeleted'
// and add [NotMapped] attribute
// then mark this property as true from the UI for deleted items.
{
// Deletes.
context.Entry(detail).State = EntityState.Deleted;
}
else
{
// Updates.
context.Entry(detail).State = EntityState.Modified;
}
}
order.OrderDetails = details;
context.SaveChanges();
}
I am using IsolatedStorageSettings to store a contact list for a key in my app. But the app only stores the list till the app is active(i.e. like navigating from one page to another). If I exit the app and again relaunch it, the stored key/contact list is not found. How do i permanently save a list for an app, till the app is installed?
Here is the code of my viewmodel, I am using:
public class ContactsViewModel : ViewModelBase
{
private static IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
private List<SavedContact> _SavedContactsList;
public ContactsViewModel()
{
Contacts cons = new Contacts();
cons.SearchAsync(String.Empty, FilterKind.None, null);
cons.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(OnContactSearchCompleted);
SaveSavedContactsCommand = new RelayCommand(OnSaveSavedContacts);
}
private List<SavedContact> GetSavedContacts()
{
if (appSettings.Contains("SavedContacts"))
{
var savedContacts = (List<SavedContact>)appSettings["SavedContacts"];
return savedContacts;
}
else
{
return new List<SavedContact>();
}
}
public RelayCommand SaveSavedContactsCommand { get; set; }
private void OnSaveSavedContacts()
{
if (!SavedContactsList.Any(x => x.IsSelected == true))
{
MessageBox.Show("Please select some contacts and then click on Save button.");
}
else
{
var selectedSavedContacts = SavedContactsList.Where(x => x.IsSelected == true).ToList();
SavedContacts = selectedSavedContacts;
MessageBox.Show("Emergency contact list added successfully.");
App.RootFrame.GoBack();
}
}
void OnContactSearchCompleted(object sender, ContactsSearchEventArgs e)
{
try
{
SavedContactsList = new List<SavedContact>();
var allContacts = new List<Contact>(e.Results.Where(x => x.PhoneNumbers.Count() > 0).OrderBy(c => c.DisplayName));
// var savedContacts = GetSavedContacts();
var savedContacts = SavedContacts;
foreach (Contact contact in allContacts)
{
SavedContact SavedContact = new SavedContact() { Contact = contact };
if (savedContacts.Any(x => x.Contact.PhoneNumbers.ElementAt(0).PhoneNumber == contact.PhoneNumbers.ElementAt(0).PhoneNumber))
{
SavedContact.IsSelected = true;
}
else
{
SavedContact.IsSelected = false;
}
SavedContactsList.Add(SavedContact);
}
}
catch (System.Exception ex)
{
MessageBox.Show("Error in retrieving contacts : " + ex.Message);
}
}
[DataMember]
public List<SavedContact> SavedContactsList
{
get { return _SavedContactsList; }
set
{
_SavedContactsList = value;
RaisePropertyChanged("SavedContactsList");
}
}
private List<SavedContact> SavedContacts
{
get
{
if (appSettings.Contains("SavedContacts"))
{
var savedContacts = (List<SavedContact>)appSettings["SavedContacts"];
return savedContacts;
}
else
{
return new List<SavedContact>();
}
}
set
{
appSettings["SavedContacts"] = value;
}
}
}
And the class SavedContact is followings:
[DataContract]
public class SavedContact : INotifyPropertyChanged
{
public SavedContact() { }
private bool _isSelected;
private Contact _contact;
[DataMember]
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
[DataMember]
public Contact Contact
{
get
{
return _contact;
}
set
{
if (_contact != value)
{
_contact = value;
OnPropertyChanged("Contact");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Where the view model is bound to a <toolKit: LongListMultiSelector />. The functionality is like, I select some contacts from my longlist multiselector and save it in storage to reuse it later. But if I exit the app and restarts it , the savedContacts returns null. While I navigate other pages in my app, the savedContacts is getting printed.
If I save a list first time , on app relaunch GetSavedContacts() returns a new list.
The problem isn't related to IsolatedStorageSettings or your RelayCommand. Looking in more detail the problem is with serialization and de-serialization of the Contact object. If you update your implementation to something like the example below, you should be fine.
public class ContactsViewModel : ViewModelBase
{
private static IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
private List<UserContact> _contactList;
public ContactsViewModel()
{
Contacts cons = new Contacts();
cons.SearchAsync(String.Empty, FilterKind.None, null);
cons.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(OnContactSearchCompleted);
SaveSavedContactsCommand = new RelayCommand(OnSaveSavedContacts);
}
public RelayCommand SaveSavedContactsCommand { get; private set; }
private void OnSaveSavedContacts()
{
if (!Contacts.Any(x => x.IsSelected == true))
{
MessageBox.Show("Please select some contacts and then click on Save button.");
}
else
{
var selectedSavedContacts = Contacts.Where(x => x.IsSelected == true).Select(x => new SavedContact{ Name = x.Contact.DisplayName, PhoneNumber = x.Contact.PhoneNumbers.ElementAt(0).PhoneNumber}).ToList();
SavedContacts = selectedSavedContacts;
MessageBox.Show("Emergency contact list added successfully.");
App.RootFrame.GoBack();
}
}
void OnContactSearchCompleted(object sender, ContactsSearchEventArgs e)
{
try
{
Contacts = new List<UserContact>();
var allContacts = new List<Contact>(e.Results.Where(x => x.PhoneNumbers.Count() > 0).OrderBy(c => c.DisplayName));
foreach (Contact contact in allContacts)
{
UserContact SavedContact = new UserContact() { Contact = contact };
if (SavedContacts.Any(x => x.PhoneNumber == contact.PhoneNumbers.ElementAt(0).PhoneNumber))
{
SavedContact.IsSelected = true;
}
else
{
SavedContact.IsSelected = false;
}
Contacts.Add(SavedContact);
}
}
catch (System.Exception ex)
{
MessageBox.Show("Error in retrieving contacts : " + ex.Message);
}
}
[DataMember]
public List<UserContact> Contacts
{
get { return _contactList; }
set
{
_contactList = value;
RaisePropertyChanged("Contacts");
}
}
public List<SavedContact> SavedContacts
{
get
{
if (appSettings.Contains("SavedContacts"))
{
var savedContacts = (List<SavedContact>)IsolatedStorageSettings.ApplicationSettings["SavedContacts"];
return savedContacts;
}
else
{
return new List<SavedContact>();
}
}
set
{
if (value != null)
{
IsolatedStorageSettings.ApplicationSettings["SavedContacts"] = value;
IsolatedStorageSettings.ApplicationSettings.Save();
}
}
}
}
public class SavedContact
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
[DataContract]
public class UserContact : INotifyPropertyChanged
{
public UserContact() { }
private bool _isSelected;
private Contact _contact;
[DataMember]
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
[DataMember]
public Contact Contact
{
get
{
return _contact;
}
set
{
if (_contact != value)
{
_contact = value;
OnPropertyChanged("Contact");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Key/Value pairs added to the applications IsolatedStorageSettings are persisted until the application is un-installed or the item is removed from the dictionary. This is true for between application restarts and when the device is powered on and off.
Your SaveList method could be simplified to the example below as it's only necessary to check .Contains when retrieving a value from the settings dictionary as it will throw an exception if the entry cannot be found.
private void SaveList()
{
appSettings["SavedContacts"] = urls;
}
Alternatively you could use a property to handle all access to IsolatedStorageSettings using something like the example below:
private IList<Contact> Contacts
{
get
{
return appSettings.Contains("SavedContacts") ? (List<Contact>)appSettings["SavedContacts"] : null;
}
set
{
appSettings["SavedContacts"] = value;
}
}