Nested object lists, last list not accessible? - c#

In the code below I try to add the AttributeValueInfoEntity to the AttributeValueExportList, however it is not showing up as a property of the ArticleAttributeExportLarge object at the place where I put //largeExportList.AttributeExportList in the code.
Am I doing something wrong, or am I missing a important nesting rule in C#?
public partial class Export_Articles_Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ArticleInfoEntity[] articleInfoList = ArticleInfoFactory.Instance.ListInfo(244);
List<ArticleExportLarge> list = new List<ArticleExportLarge>();
foreach (ArticleInfoEntity aie in articleInfoList)
{
ArticleExportLarge largeExportList = new ArticleExportLarge(aie);
largeExportList.AttributeExportList = new List<ArticleAttributeExportLarge>();
List<AttributeInfoEntity> attributeInfoList = AttributeInfoFactory.Instance.ListByArticle(aie.ArticleId);
foreach (AttributeInfoEntity attributeInfo in attributeInfoList)
{
largeExportList.AttributeExportList.Add(new ArticleAttributeExportLarge(attributeInfo));
List<AttributeValueInfoEntity> ArticleValueInfoList = AttributeValueInfoFactory.Instance.ListByArticleAndAttribute(aie.ArticleId, attributeInfo.AttributeId);
foreach (AttributeValueInfoEntity avie in ArticleValueInfoList)
{
//largeExportList.AttributeExportList
}
}
list.Add(largeExportList);
}
}
}
public class ArticleExportLarge
{
private ArticleInfoEntity articleInfo;
private List<ArticleAttributeExportLarge> attributeExportList;
public ArticleExportLarge(ArticleInfoEntity articleInfo)
{
this.articleInfo = articleInfo;
}
[XmlElement(ElementName = "attributeList")]
public List<ArticleAttributeExportLarge> AttributeExportList
{
get { return attributeExportList; }
set { attributeExportList = value; }
}
}
public class ArticleAttributeExportLarge
{
private AttributeInfoEntity attributeInfo;
private List<ArticleAttributeValueExportLarge> attributeValueExportList;
public ArticleAttributeExportLarge(AttributeInfoEntity attributeInfo)
{
this.attributeInfo = attributeInfo;
}
[XmlElement(ElementName = "attributeValueList")]
public List<ArticleAttributeValueExportLarge> AttributeValueExportList
{
get { return attributeValueExportList; }
set { attributeValueExportList = value; }
}
}
public class ArticleAttributeValueExportLarge
{
private AttributeValueInfoEntity attributeValueExportEntity;
public AttributeValueInfoEntity AttributeValueExportEntity
{
get { return attributeValueExportEntity; }
set { this.attributeValueExportEntity = value; }
}
}

I found my error. I need to iterate through the secondary list largeExportList.AttributeExportList if I want to access the objects in that list.

Related

How can I restrict who can make changes using interfaces?

I have a IHasTag interface, a TaggableItem class that implements IHasTag and a TaggingManager class which I want to make it the only one responsible for setting or clearing the Tags but I've been struggling all day trying to implement but with no luck.
How can I make this possible?
public class TaggableItem : TaggingManager.IHasTag
{
public string Tag { get; } // read only
}
public class TaggingManager
{
public interface IHasTag
{
string Tag { get; }
}
private List<IHasTag> _taggedItems = new();
public void TagItem(IHasTag item, string tag)
{
item.Tag = tag; // not working
_taggedItems.Add(item);
}
public void ClearAllTags()
{
foreach (var item in _taggedItems)
{
item.Tag = "": // not working
}
_taggedItems.Clear();
}
}
EDIT
I followed Thomas' suggestion and this is what I end up doing. I know it's not perfect, though. Thank you all for your advices.
public interface ITaggable
{
string? Tag { get; }
}
public interface ISelectable
{
bool IsSelected { get; }
}
public interface IItem : ITaggable, ISelectable
{
}
public class Item : IItem
{
protected Item() { }
public bool IsSelected { get; set; }
public string Tag { get; set; } = string.Empty;
// 'Item' will be created here returning IItem.
// So, unless you 'cast' it, you can't set 'Tag' or 'IsSelected'.
public static IItem CreateItem() => new Item();
}
public class SelectionManager
{
protected List<object> _items = new();
public void Select(ISelectable item)
{
if (item is Item selectable)
{
selectable.IsSelected = true;
_items.Add(item);
}
}
public void Unselect(ISelectable item)
{
if (item is Item selectable)
{
selectable.IsSelected = false;
_items.Remove(item);
}
}
}
public class TaggingManager
{
private List<object> _items = new();
public void Tag(ITaggable item, string tag)
{
if (item is Item taggable)
{
taggable.Tag = tag;
_items.Add(item);
}
}
public void Untag(ITaggable item)
{
if (item is Item taggable)
{
taggable.Tag = string.Empty;
_items.Remove(item);
}
}
}
My suggestion would be to have two interfaces for two purposes: reading and writing.
public interface IHasTag // interface for reading. Maybe IReadTag
{
string Tag { get; }
}
public interface ITagChange // interface for writing. Maybe IWriteTag
{
string Tag { set; }
}
public class TaggableItem : IHasTag, ITagChange // implement both
{
public string Tag { get; set; }
}
// Tagging manager gets write access (ITagChange)
public class TaggingManager
{
private List<ITagChange> _taggedItems = new ();
public void TagItem(ITagChange item, string tag)
{
item.Tag = tag;
_taggedItems.Add(item);
}
public void ClearAllTags()
{
foreach (var item in _taggedItems)
{
item.Tag = "";
}
_taggedItems.Clear();
}
}
// Everyone else has read access only (IHasTag)
class SomeoneElse
{
private List<IHasTag> _taggedItems = new ();
public void DoSomething(IHasTag item)
{
_taggedItems.Add(item);
var tag = item.Tag; // do something with the tag
}
}
class Instantiation
{
public void Main()
{
TaggableItem x = new TaggableItem();
TaggingManager m = new TaggingManager();
m.TagItem(x, "name");
SomeoneElse s = new SomeoneElse();
s.DoSomething(x);
}
}
One possible option is to move the "getter" also to the TagManager. Then the manager is responsible for the tags. The object itself does not even need to know about the tags.
You still can restrict this by exchanging object with an interface.
public class TagManager
{
private Dictionary<object, string> _tagedItems = new Dictionary<object, string>();
public bool HasTag(object item)
{
return _tagedItems.ContainsKey(item);
}
public string GetTag(object item)
{
return _tagedItems[item];
}
public void SetTag(object item, string tag)
{
if(!HasTag(item))
{
_tagedItems.Add(item, tag);
}
else
{
_tagedItems[item] = tag;
}
}
}
I don't think that this is really a answer to the OP, but one possible solution for the underlying problem.

Layout.RaiseChild(View) Method in Xamarin.Forms

RelativeLayout :
How Can we write Layout.RaiseChild(View) Method in XAML?
( or )
How can we write in ViewModel?
namespace MatchesProject.ViewModels
{
public class MatchesViewModel : BindableObject
{
private int _lastItemAppearedIdx = 0;
private bool IsLastDirectionWasUp = false;
private bool stackMatchTypesIsVisible { get; set; }
public bool StackMatchTypesIsVisible
{
get => stackMatchTypesIsVisible;
set
{
if (value == stackMatchTypesIsVisible)
return;
stackMatchTypesIsVisible = value;
OnPropertyChanged();
}
}
public ObservableCollection<MatchProfile> MatchesList { get; set; }
public ICommand ItemDisappearingCommand
{
get;
private set;
}
private void ItemDisappearing(object obj)
{
try
{
MatchProfile profile = obj as MatchProfile;
if (MatchesList != null && MatchesList.Contains(profile))
{
if (MatchesList.Contains(profile))
{
var currentIdx = MatchesList.IndexOf(profile);
if (currentIdx > _lastItemAppearedIdx)
{
StackMatchTypesIsVisible = false;
IsLastDirectionWasUp = true;
}
else if (IsLastDirectionWasUp)
{
IsLastDirectionWasUp = false;
StackMatchTypesIsVisible = true;
MessagingCenter.Send(this, "RaiseChild", "");
}
_lastItemAppearedIdx = currentIdx;
}
}
}
catch (Exception) { }
}
public MatchesViewModel()
{
ItemDisappearingCommand = new Command(ItemDisappearing);
MatchesList = new ObservableCollection<MatchProfile>();
BindingContext = MatchesList;
}
}
}
//MatchesPage.xaml.cs
public partial class MatchesPage : ContentPage
{
public MatchesPage()
{
InitializeComponent();
MessagingCenter.Subscribe<MatchesPageViewModel>(this, "RaiseChild", (page) => { relativeCompletePage.RaiseChild(stackMatchTypes); });
}
}
I am using RelativeLayout and inside this more than 2 stack layouts with one Listview. While Scrolling listview, Some cases I need to bring front the StackLayout. That's why I have used MessageCenter. I need to know, Is there have any other better option to fix?

How do you remove items from several list boxes by removing one item associated with them all?

I want to remove Server1 from it's listbox, i'd want it to remove all the other items in the other listboxes associated with it as well. ("Server-Domain1" and all the "Server1-Domain1-CSR's"). Is there a way to do this?
To "bind" these list boxes i just used:
domainListBox.Items.Add((serverListBox.SelectedItem) + "-" + (this.domainTextbox.Text));
and
csrListBox.Items.Add((domainListBox.SelectedItem) + ("-CSR-1"));
csrListBox.Items.Add((domainListBox.SelectedItem) + ("-CSR-2"));
csrListBox.Items.Add((domainListBox.SelectedItem) + ("-CSR-3"));
If you choose your server from servers listbox, you can remove associated items like this (lets pretend there is some remove button, you selecting domain from listbox and clicking on remove button):
private void removeBtn_Click(object sender, EventArgs e)
{
List<string> items = csrListBox.Items.Cast<string>().ToList();
foreach (string item in csrListBox.Items)
{
Regex regex = new Regex(#"^" + domainListBox.SelectedItem + #"\w*");
Match match = regex.Match(item);
if (match.Success)
{
items.Remove(item);
}
}
csrListBox.DataSource = items;
}
Hope it helps.
Create a class that encapsulates the Server and it's details such as Domains and Csrs. Create a list of Servers and bind it to the first List Box. Then bind the other two List Boxes to the currently selected item of the first List Box. The end result may look like this:
serverListBox.ItemSource = Servers;
domainListBox.ItemSource = (serverListBox.SelectedItem as Server).Domains;
csrListBox.ItemSource = (serverListBox.SelectedItem as Server).Csrs;
This enables you to set the different List Boxes data without writing a lot of code that could make it unmaintainable.
Well, seeing your code, you just have to do something like this when you remove your server:
string server = serverListBox.SelectedItem as string;
serverListBox.Remove(server);
for (int i = domainListBox.Items.Count -1; i >-1; i--)
{
if (domainListBox.Items[i].ToString().StartsWith(server))
{
string domain = domainListBox.Items[i].ToString();
domainListBox.Items.RemoveAt(i);
for (int j = csrListBox.Items.Count-1; j > -1; j--)
{
if (csrListBox.Items[j].ToString().StartsWith(domain))
{
csrListBox.Items.RemoveAt(j);
}
}
}
}
Edit I have tested it now, this should work
a better option would be using observable pattern as
public class ObservableObject<T> : IList, IListSource
{
protected BindingSource src = null;
List<ListControl> Subscribers;
ObservableCollection<T> Data = new ObservableCollection<T>();
public bool IsReadOnly
{
get
{
return false;
}
}
public bool IsFixedSize
{
get
{
return false;
}
}
public int Count
{
get
{
return Data.Count;
}
}
public object SyncRoot
{
get
{
throw new NotImplementedException();
}
}
public bool IsSynchronized
{
get
{
throw new NotImplementedException();
}
}
public bool ContainsListCollection
{
get
{
return true;
}
}
object IList.this[int index]
{
get
{
return Data[index];
}
set
{
Data[index] = (T)value;
}
}
public T this[int index]
{
get
{
return Data[index];
}
set
{
Data[index] = value;
}
}
public ObservableObject()
{
Data.CollectionChanged += Domains_CollectionChanged;
Subscribers = new List<ListControl>();
src = new BindingSource();
src.DataSource = Data;
}
private void Domains_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
src.ResetBindings(false);
}
public virtual void Subscribe(ListBox ctrl)
{
this.Subscribers.Add(ctrl);
//ctrl.DataBindings.Add(new Binding("SelectedValue", src, "Name",
// true, DataSourceUpdateMode.Never));
ctrl.DataSource = src;
}
public int Add(object value)
{
Data.Add((T)value);
return Data.Count - 1;
}
public bool Contains(object value)
{
return Data.Contains((T)value);
}
public void Clear()
{
Data.Clear();
}
public int IndexOf(object value)
{
return Data.IndexOf((T)value);
}
public void Insert(int index, object value)
{
Data.Insert(index, (T)value);
}
public void Remove(object value)
{
Data.Remove((T)value);
}
public void RemoveAt(int index)
{
Data.RemoveAt(index);
}
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
public IEnumerator GetEnumerator()
{
return Data.GetEnumerator();
}
public IList GetList()
{
return Data;
}
}
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get { return _name; }
set
{
this._name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
}
public class CSR : BaseModel
{
public override string ToString()
{
return Name;
}
}
public class Domain : BaseModel
{
public ObservableObject<CSR> CSRs { get; set; }
public Domain()
{
CSRs = new ObservableObject<CSR>();
}
public override string ToString()
{
return Name;
}
}
public class Server : BaseModel
{
public ObservableObject<Domain> Domains { get; set; }
public Server()
{
Domains = new ObservableObject<Domain>();
}
public override string ToString()
{
return Name;
}
}
public class DataModel : BaseModel
{
public ObservableObject<Server> Servers
{
get; set;
}
public DataModel()
{
Servers = new ObservableObject<Server>();
}
public override string ToString()
{
return Name;
}
}
on Form
DataModel m = new DataModel();
public Form1()
{
...
m.Servers.Subscribe(listBox1);
}
private void listBox2_Click(object sender, EventArgs e)
{
if (listBox2.SelectedIndex >= 0)
{
m.Servers[listBox1.SelectedIndex].Domains[listBox2.SelectedIndex].CSRs.Subscribe(listBox3);
}
}
private void listBox1_Click(object sender, EventArgs e)
{
if (listBox1.SelectedIndex >= 0)
{
m.Servers[listBox1.SelectedIndex].Domains.Subscribe(listBox2);
}
}
you dont have to write code to remove items from forms in this perspective.removing object from model would do

C# Communication between parent form and child form

My project has two classes. The first class has information about continents and it contains also a list of objects of countries (another class).
I also declared a list of continents that contains all the continents.
I've succeeded in filling the list from a file, and succeeded to show them in a DataGridView in the same form. But the problem is that I didn't find a way to show them in a child form that contains a DataGridView.
So, how can I transfer the list of continents to the child form so that I can be able to show them in it?
I tried serialiization and deserialization, but it didn't work, I just see the name of members of continent class and nothing else.
Here are the two class and code of toolstrip that show the child form:
// first class of continent
namespace WindowsFormsApplication1
{
[Serializable]
class continent
{
//champs
private string nomc;
public string Nomc
{
get { return this.nomc; }
}
private string sup;//SUP
public string Superficie
{
get { return this.sup; }
set { this.sup = value; }
}
private string pop;//POP
public string Population
{
get { return this.pop; }
set { this.pop = value; }
}
private string dens;//DENS :
public string Densité
{
get { return this.dens; }
set { this.dens = value; }
}
private string nbp;//NBP : 54 :
public string nombre_de_Pays
{
get { return this.nbp; }
set { this.nbp = value; }
}
private string fus;//FUS )
public string Fuseaux_horaires
{
get { return this.fus; }
set { this.fus = value; }
}
private string pnb;//PNB
public string PNB_habitant
{
get { return this.pnb; }
set { this.pnb = value; }
}
//constructeur
public continent(string nom)
{
this.nomc = nom;
}
public continent()
{
// TODO: Complete member initialization
}
//list of countries of that continent
public List<country> listep = new List<country>();
}
// class of countries
namespace WindowsFormsApplication1
{
[Serializable]
class country
{
//champs
private string nom_p;
public string Nom_pays
{
get { return this.nom_p; }
set { this.nom_p = value; }
}
private string cap;//PCAP
public string Capitale
{
get { return this.cap; }
set { this.cap = value; }
}
private string sup;// PSUP
public string Superficie
{
get { return this.sup; }
set { this.sup = value; }
}
private string reg;// REG
public string Régime_politique
{
get { return this.reg; }
set { this.reg = value; }
}
private string dev;//PDEV nationale
public string Devise
{
get { return this.dev; }
set { this.dev = value; }
}
private string hym;// PHYM
public string Hymne
{
get { return this.hym; }
set { this.hym = value; }
}
private string lg;// PLG
public string Langue
{
get { return this.lg; }
set { this.lg = value; }
}
private string mo;// PMO
public string Monnaie
{
get { return this.mo; }
set { this.mo = value; }
}
private string de;
public string PDE
{
get { return this.de; }
set { this.de = value; }
}
//constructeur
public country (string nom)
{
this.nom_p = nom;
}
}
}
and the code in the form is
//liste of contnents
List<continent> listec = new List<continent>();
// i filled it from a file
//here the code of toolstrip that open the childform
private void listeContinentToolStripMenuItem_Click(object sender, EventArgs e)
{
listecont flc = new listecont();
flc.ShowDialog();
flc.MdiParent = this;
}
In your child form, add an overload to the Form constructor that takes a Form as an argument. Then when you create your child form, you can pass in an instance of your current (parent) form like, listecont flc = new listecont(this); where this is a reference of your parent form. Now your child form can make calls to parentForm.Textbox.Text = "blablabal" or what ever object you want to interact with.
Why not just add a constructor to the listecont class that takes a List<continent>? Then, the child form will have the data when it's constructed.
in your MDI child add a method:
public void SetContinentData(List<continent> list)
{
// add your DataSource to the grid
// f.e.:
dataGridView.DataSource = list;
}
and in your Toolstrip handler:
private void listeContinentToolStripMenuItem_Click(object sender, EventArgs e)
{
listecont flc = new listecont();
flc.SetContinentData(listec);
flc.ShowDialog();
flc.MdiParent = this;
}

Save List to IsolatedStorageSettings

I have this class :
class LyricsItem
{
public LyricsItem()
{
}
public LyricsItem(LyricsItem item)
{
this.searchUrl = item.searchUrl;
this.croppingRegex = item.croppingRegex;
}
private string _searchUrl;
private string _croppingRegex;
public string searchUrl
{
get { return _searchUrl; }
set { _searchUrl = value; }
}
public string croppingRegex
{
get { return _croppingRegex; }
set { _croppingRegex = value; }
}
}
And this is Array with items LyricsItem :
public List<LyricsItem> lyricsArray;
This is how i add items to the array :
LyricsItem item = new LyricsItem();
item.croppingRegex = croppingRegex;
item.searchUrl = searchurl;
lyricsArrayTmp.Add(item);
And i want to add it to IsolatedStorageSettings:
IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;
if (appSettings.Contains("lyricsData"))
{
appSettings["lyricsData"] = lyricsArray;
}
else
{
appSettings.Add("lyricsData", lyricsArray);
}
appSettings.Save();
But When i get to save the IsolatedStorageSettings i get this exception:
The collection data contract type 'System.Collections.Generic.List`1[[**********, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' cannot be deserialized because it does not have a public parameterless constructor. Adding a public parameterless constructor will fix this error. Alternatively, you can make it internal, and use the InternalsVisibleToAttribute attribute on your assembly in order to enable serialization of internal members - see documentation for more details
You can't serialize a private class in the ApplicationSettings. Declare it as public instead:
public class LyricsItem
{
public LyricsItem()
{
}
public LyricsItem(LyricsItem item)
{
this.searchUrl = item.searchUrl;
this.croppingRegex = item.croppingRegex;
}
private string _searchUrl;
private string _croppingRegex;
public string searchUrl
{
get { return _searchUrl; }
set { _searchUrl = value; }
}
public string croppingRegex
{
get { return _croppingRegex; }
set { _croppingRegex = value; }
}
}

Categories