Cannot access List from a class within an Array - c#

I am a beginner developer, and would very much appreciate if you can help me figure out the problem which is in my code. The code is particularly confusing, mainly because it derives from a framework. The comments should be able to somewhat allow us to understand.
// Create an IBindable List
public static List<IBindable> KyzerBindables = new List<IBindable>();
// Attach elements to a list, for better control over all of them
internal static void AttachBindablesToList(IReadOnlyList<Drawable> children)
{
// For all the children classes located in Drawable list
for (int i = 0; i < children.Count; i++) // children.Count returns 4
{
// For all of the SettingsSubsection which are present in the Drawable array
for (int l = 0; l < (children[i] as SettingsSubsection).Children.Count; l++) // (children[i] as Subsection).Children.Count returns 0.
{
// Get a specific element
var element = (children[i] as SettingsSubsection).Children[l];
// if is a SettingsCheckbox
if (element.GetType() == typeof(SettingsCheckbox))
KyzerBindables.Add((element as SettingsCheckbox).Bindable);
}
}
}
// in another class
public class KyzerSection: SettingsSection
{
public KyzerSection()
{
Children = new Drawable[]
{
new KyzerMiscellaneous(),
};
...AttachElementsToList(Children);
}
}
public class KyzerMiscellaneous: SettingsSubsection
{
[BackgroundDependencyLoader] // Calls load, framework thing.
private void load(OsuConfigManager config)
{
Children = new Drawable[]
{
new SettingsCheckbox
{
LabelText = "Something here",
Bindable = new BindableBool(false),
}
};
}
}
My problem is, the second for loop does not even initiate for the AttachBindablesToList. For whatever particular reason, it isn't recieving a count. I am uncertain of what I am doing wrong.
Edit:
If, in any way, the GitHub repository issue can clear some issues up, please feel free to navigate there and check the commit which contains these changes. https://github.com/Frontear/osuKyzer/issues/3

After reviewing your github repository, I believe the issue is caused at:
private void load(params here)
The above is not being called at the time of AttachBindablesToList. This results in an empty
(children[i] as SettingsSubsection).Children.Count
The best option is to create an empty instantiation method
public KyzerMiscellaneous() { /* create Drawable elements */ }
// then
[BackgroundDependancyLoader]
private void load(params here) { /* doSomething */ }
This will allow access to the children list since it has been initialized before, which therefore allows the second loop to correctly function, and pushes IBindables to your list.

Related

How to build a custom loop activity in Elsa workflows

I'm trying to make a custom activity that will eventually do a complicated database query or API call to get a bunch of records and loop over them. I'm sure it could be done with the built in flow control activities, but I want to make this usable by non-programmers who don't know or care what a foreach loop is, so putting a lot of functionality into one box is good.
My first attempt was to inherit from ForEach and do some initialization before letting OnExecute do its thing, but the result feels somewhat hacky.
public class FancyForEach : ForEach
{
private bool? Initialized
{
get
{
return GetState<bool?>("Initialized");
}
set
{
SetState(value, "Initialized");
}
}
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{
if (Initialized != true)
{
Items = GetThingsFromDatabase();
Initialized = true;
}
return base.OnExecute(context);
}
protected List<DatabaseThings> GetThingsFromDatabase()
{
// Fancy stuff here, including paging eventually.
}
}
It seems like it would be a little cleaner to instantiate a ForEach somewhere within the activity rather than inherit from it, but I can't puzzle out a way to make that work. I imagine a decent solution would be to trigger another workflow for each record, but I'd rather not do that, again to make this easy to digest for people who aren't programmers.
Can anyone offer a suggestion on the best way to make this work? This is my first project using Elsa, so maybe I'm approaching it from an entirely wrong direction!
If I understand correctly, your activity is responsible for loading in the data and looping over it, while the user of the activity should be able to specify what happens in each iteration.
If so, then you might implement something like this:
[Activity(
Category = "Control Flow",
Description = "Iterate over a collection.",
Outcomes = new[] { OutcomeNames.Iterate, OutcomeNames.Done }
)]
public class FancyForEach : Activity
{
private bool? Initialized
{
get => GetState<bool?>();
set => SetState(value);
}
private IList<DatabaseThings>? Items
{
get => GetState<IList<DatabaseThings>?>();
set => SetState(value);
}
private int? CurrentIndex
{
get => GetState<int?>();
set => SetState(value);
}
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{
if (Initialized != true)
{
Items = GetThingsFromDatabase();
Initialized = true;
}
var collection = Items.ToList();
var currentIndex = CurrentIndex ?? 0;
if (currentIndex < collection.Count)
{
var currentValue = collection[currentIndex];
var scope = context.CreateScope();
scope.Variables.Set("CurrentIndex", currentIndex);
scope.Variables.Set("CurrentValue", currentValue);
CurrentIndex = currentIndex + 1;
context.JournalData.Add("Current Index", currentIndex);
// For each iteration, return an outcome to which the user can connect activities to.
return Outcome(OutcomeNames.Iterate, currentValue);
}
CurrentIndex = null;
return Done();
}
protected List<DatabaseThings> GetThingsFromDatabase()
{
// Fancy stuff here, including paging eventually.
}
}
This example loads the database items into memory once and then stores this list in workflow state (via Items) - which may or may not be desirable, since this has the potential of increasing the size of the workflow instance significantly depending on the size of each record and number of records.
A more scalable approach would be to load just one item per iteration, keeping track of the current index that was loaded, incrementing it (i.e. pagination with a page size of 1).

How to move onto the next item in a list

So as part of my training I have to create a program which utilizes the Xbox Api. It works so that the user searches for a Gamer tag and it brings up all of that users recently uploaded game clips. You can then select a clip and it will play within the Windows Media Player control I added.
Now I am trying to set it to auto play the next video. How would I fill out the following method to autoplay the next item in the list keeping in mind that it is not looping through all the videos in the list, just from where the user selects from.
private void wmpClip_PlayStateChange(object sender,
AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if (wmpClip.playState == WMPLib.WMPPlayState.wmppsMediaEnded)
{
//play next video in list
}
}
So the following code is part of the search method when searching for clips:
foreach (var video in videos)
{
ctrlSearchResults searchResult = new ctrlSearchResults();
searchResult.SetDetails(video);
flpSearchResults.Controls.Add(searchResult);
searchResult.OnVideoSelected += SearchResult_OnVideoSelected;
//Limit to save api requests
if (flpSearchResults.Controls.Count == 3)
{
break;
}
}
Then when the user clicks on a video from the list on the Flow Layout Panel, the following code is run:
private void SearchResult_OnVideoSelected(Video obj)
{
wmpClip.Visible = true;
wmpClip.URL = obj.gameClipUris[0].uri;
pnlVideoInfo.Visible = true;
lblClipName.Visible = true;
lblActualLength.Text = obj.durationInSeconds.ToString();
lblActualSize.Text = (obj.gameClipUris[0].fileSize / 1024 / 1024).ToString() + "mb";
lblActualDate.Text = obj.datePublished.ToString();
lblActualGame.Text = obj.titleName;
lblActualViews.Text = obj.views.ToString();
lblActualRating.Text = obj.rating.ToString();
lblActualLikes.Text = obj.likeCount.ToString();
lblClipName.Text = obj.clipName;
GamerCard gamer = _clipsApi.GetGamerCardByXUID(obj.xuid.ToString());
pnlGamerInfo.Visible = true;
pbGamerPic.Load(gamer.gamerpicLargeSslImagePath);
lblGamerTag.Text = gamer.gamertag;
lblGamerScore.Text = gamer.gamerscore.ToString();
lblActualLocation.Text = gamer.location;
txtBio.Text = gamer.bio;
}
I hope this makes sense.
You should work on your separation of concerns: divide your big problem into smaller problems, and invent for every smaller problem a separate fairly independent solution. This makes the implementations easier to understand, easier to reust, test, change etc.
So let's separate your concerns!
Apparently you have something to fetch all your videos as a sequence:
IEnumerable<Video> GetAllVideos() {...}
How this is implemented is up to you. I assume you have no duplicates in this: every Video is either selected or not selected, you can not have the same video selected as well as non-selected.
Let's create a collection class for videos where you can select and unselect videos.
In fact: let's make it reusable: a generic collection class that contains objects that
can be selected and unselected:
public SelectableCollection<T>
// only if desired
: IReadonlyCollection<T>, IReadonlyList<T>, IEnumerable<T>
{
private readonly Dictionary<T, bool> allItems;
public SelectableCollection() : this(Enumerable.Empty<T>()) {}
public SelectableCollection(IEnumerable<T> collection) : this (collection, null) {}
public SelectableCollection(IEnumerable<T> source, IEqualityComparer<T> comparer = null)
{
// TODO: check source not null
if (comparer == null) comparer = EqualityComparer<T>.Default;
// initially nothing selected:
this.AllItems = source.ToDictionary(video => video, false, comparer);
}
Select and Unselect:
bool IsSelected(T item)
{
// TODO: decide what if item does not exist
return this.allItems[item];
}
bool Select(T item)
{
// TODO: decide what if item does not exist
this.allItems[item] = true;
}
bool UnSelect(T item)
{
// TODO: decide what if item does not exist
this.allItems[item] = false;
}
IEnumerable<T> SelectedItems => this.allItems
// Key is T, Value is boolean Selected
.Where(keyValuePair => keyValuePair.Value)
.Select(keyValuePair => keyValuePair.Key);
TODO: implement IReadOnlyCollection, etc. Use this.allItems.Keys to get all items.
Your forms class:
private SelectableCollection<Video> videos = new SelectableCollection(this.GetAllVideos());
Select and Unselect:
bool IsSelected(Video video)
{
return this.videos.IsSelected(video);
}
bool Select(Video video)
{
this.videos.Select(video);
}
bool UnSelect(Video video)
{
this.videos.UnSelect(video);
}
IEnumerable<Video> SelectedVideos => this.videos.SelectedItems;
Start / Stop / Continue playing Videos:
class VideoPlayer
{
private List<Video> VideosToPlay {get; set;}
private int IndexNextVideoToPlay {get; set;}
void StartPlayingVideos(IEnumerable<Video> videos)
{
this.VideosToPlay = this.SelectedVideos.ToList();
this.IndexNextVideoToPlay = 0;
}
private void PlayNextVideo()
{
if (this.IndexNextVideoToPlay < this.VideosToPlay.Count)
{
this.Play(this.VideosToPlay[this.IndexNextVideoToPlay];
++this.IndexNextVideoToPlay;
}
}
}
Examples:
private void OnButtonClicked(object sender, EventArgs e)
{
// user finished selecting videos in the listbox. Mark them as selected
IEnumerable<Video> selectedVideos = this.listBox1.SelectedItems.Cast<Video>();
// TODO: select all selectedVideos; Unselect all not-selected videos.
}
private void OnVideoFinished(object sender, EventArgs e)
{
this.VideoPlayer.PlayNextVideo();
}
Conclusion: divide your problem into small sub problems. Don't hesitate to create a class that solves your sub problem. Usually these classes will only have one small task and barely depend on other classes. Hence your class will be easy to understande, easy to create, easy to reuse, easy to test and maintain.

Continuously adding and removing to flow-layout panel win forms

I have a c# win forms application which has a flowLayoutPanel in it.
I need to update all the children in this panel every second.
here is my current code which gets called in a system timer every 1 seconds:
public void RefreshReceiversPage()
{
if (checkBox_enableReceivers.Checked)
{
var receivers = OutgoingReceiverManager.GetCopyOfActiveRecieverHolders();
for (int i = 0; i < flowLayoutPanel_receivers.Controls.Count; i++)
{
var tmp = flowLayoutPanel_receivers.Controls[i];
flowLayoutPanel_receivers.Controls[i].Dispose();
tmp.Dispose();
}
flowLayoutPanel_receivers.Controls.Clear();
foreach (var item in receivers.ToList())
{
var tmpUc = new ucReceiverItem(item);
if (flowLayoutPanel_receivers != null)
{
try
{
flowLayoutPanel_receivers.Controls.Add(tmpUc);
}
catch
{
}
}
}
receivers = null;
}
}
now this code works perfectly for about 2 minutes and then all of a sudden I start getting error creating window handle Hence the reason for my try catch in the code above.
But after this happens the pane view goes funny and I cant recover it without starting up the program again.
I have searched high and low and I cant seem to find anything on the exception being thrown?
All that I can think is that im maybe not disposing of object properly and that its running out of memory some where?
Does any one have any suggestions or solutions?
EDIT:
here is UCRecieverItem:
public partial class ucReceiverItem : UserControl
{
public ucReceiverItem(ReceiverHolder item)
{
InitializeComponent();
ConstructItem(item);
item = null;
}
private void ConstructItem(ReceiverHolder item)
{
label_name.Text = item.ReceiverDb.Name;
label_numberOfConnections.Text = item.ReceiverOutgoingConnectionManager.GetNumberOfConnections().ToString();
label_mrFilters.Text = item.ReceiverDb.MrFilters;
label_multipleConnections.Text = item.ReceiverDb.MultipleConnections.ToString();
//
int count = item.GetActiveBufferCount();
int size = item.GetActiveBufferSize();
//
label_bufferCount.Text = count + #" / " + size;
progressBar_buffer.Maximum = size;
progressBar_buffer.Minimum = 0;
progressBar_buffer.Value = count;
}
}
This code is a problem:
for (int i = 0; i < flowLayoutPanel_receivers.Controls.Count; i++)
{
var tmp = flowLayoutPanel_receivers.Controls[i];
flowLayoutPanel_receivers.Controls[i].Dispose();
tmp.Dispose();
}
flowLayoutPanel_receivers.Controls.Clear();
It is only disposing half of the controls in the container. The other half get removed by the Controls.Clear(); call, but those controls do not get disposed — so they still exist and are using up memory.
Doing this every second compounds the problem: that's potentially a lot of controls.
The immediate work-around would be to properly dispose of the controls:
while (flowLayoutPanel_receivers.Controls.Count > 0) {
flowLayoutPanel_receivers.Controls[0].Dispose();
}
After that, I would question the need to do this every second — seems like a harsh environment for a user to work in.

update an ObservableCollection with a BlockingCollection

I subscribe to a service that will raise an Event when a new element is received, I add this element to a BlockingCollection.
I have a second thread running that will loop the BlockingCollection to add/update the element in an observable collection.
The problem is how do you do add in the ObservableCollection?
I know I can't just do an .add on this type of collection, as it needs to be done on the UI thread. So I tried using different ObservableCollection subclass that use the dispatcher to marshal the adding of element, but every single time, I end up with the same error
"An unhandled exception of type 'System.StackOverflowException'
occurred in Unknown Module."
with a troubleshooting tips as:
make sure you don't have an infinite loop or inifiniterecursion.
Well the thing is, I do have some kind of infinite loop actually, as the BlockingQueue is receiving new elements all the time, like 5 to 10 per sec.
I won't get the exception if I don't bind a control to the observablecollection though, or if I use a List instead.
Class ElementHolder
{
private ExternalService _externalService;
private ObservableCollection<Element> _elementsList = new ObservableCollection<Element>();
private BlockingCollection<Element> _elementsReceived = new BlockingCollection<Element>();
public ObservableCollection<Element> ElementsList
{
get
{
return _elementList;
}
set
{
_elementList = value;
}
}
public ElementHolder()
{
Task.Factory.StartNew(ReadElements);
_externalService = new ExternalService();
_externalService.ReceivedNewElement += new Action<Element>(o => _elementsReceived.Add(o));
_externalService.Subscribe();
}
private void ReadElements()
{
foreach (Element element in _elementsReceived.GetConsumingEnumerable())
{
Element item = _elementsList.FirstOrDefault(o => o.ID == element.ID);
if (item == null)
{
_elementList.Add(element);
}
else
{
item.Update(element);
}
}
}
EDIT
The bug disappeared by itself, when i was tracking it down. I was trying to make things simpler to really understand where the issue was, and then it started to work. When putting things back together it still works... BUT it comes back time to time, for what seems unrelated reason like adding a style to my listview. I'm starting to think there's an issue in the third party dll.
This is a perfect example of where the Reactive Extensions are a very useful and ingenious tool. There is a pretty steep learning curve to using them, but since you have a specific case here, I will insert the reactive code that will achieve your goal, assuming I understand your goal correctly.
Note that you will need to install the Reactive Extensions, and you will need two using statements (System.Reactive.Linq and System.Reactive.Subjects) and you will need to reference System.Reactive and System.Reactive.Windows.Threading dlls. See Reactive on MSDN.
class ElementHolder
{
public ObservableCollection<Element> ElementsList { get; set; }
private ExternalService _externalService = new ExternalService();
private IDisposable _elementSubscription;
private Subject<Element> _elementSubject = new Subject<Element>();
public ElementHolder()
{
_externalService.ReceivedNewElement += _elementSubject.OnNext;
_externalService.Subscribe();
ElementList = new ObservableCollection<Element>();
_elementSubscription = _externalService.ObserveOnDispatcher().Subscribe(NextElement);
}
private void NextElement(Element e)
{
Element item = ElementsList.FirstOrDefault(o => o.ID == element.ID);
if (item == null) {
_elementList.Add(element);
}
else {
item.Update(element);
}
}
}
There's a small error in the answer. See the corrected line below:
_elementSubscription = _elementSubject.ObserveOnDispatcher().Subscribe(NextElement);
It took me a while to figure this out, but rmayer06's answer solved my problem. I can't comment on answers or I would have.

How can I check if the clicked object is the topmost in C# XNA?

I'm trying to reproduce the simple window interface objects in C# XNA like labels, listboxes, textboxes and panels. All objects consequentially derive from basic abstract class (XNAInterfaceObject) that draws an object and updates it. In update method it interacts with a mouse and keyboard and raises various events.
The problem is when two interface objects are one over another (e.g. popup context menu over listbox) and both have non-null events - the code fires both events, when I just need the event of the topmost object. How can I check which object is the topmost? Or make somehow the top object overlap the lower. I thought about global variable which would keep the reference for the last clicked object, and other objects would check if this variable is null to proceed with their events, but I think it is a rough solution and there exists far more elegant one.
Sorry for my language, I'm not a native English-speaker.
I would probably break this issue down into two components:
Determining the order of interface objects.
Only triggering the events on the top-most object when there's an overlap.
Addressing part one is simple. Include a 'layer' field/property in the base class that specifies the depth of the object. In most game node classes I include this regardless, as it's useful in drawing. You may want a separate layering system for interface ordering if things get a bit more complex, and the downside to this approach is that you can get overlaps in which the layers are the same.
As #Attila has suggested, you can otherwise manage a Z-Ordered list of interface elements. In this case ordering is managed by index, and it's easy to manage but you can't also use this information for drawing without some additional processing and it won't be as quick as a simple value comparison.
Property
public class InterfaceComponent
{
// Class members...
private float layer;
public float Layer { get { return layer; } set { layer = Math.Abs(value); } }
public bool InFrontOf(InterfaceComponent other) { return this.Layer < other.Layer; }
}
Z-Ordered List
public class InterfaceComponent
{
private static List<InterfaceComponent> zOrder = new List<InterfaceComponent>();
// Class Members....
public InterfaceComponent()
{
// Construct class...
zOrder.Add(this);
}
private void SetZOrder(int order)
{
if (order < 0 || order >= zOrder.Count)
return;
zOrder.Remove(this);
zOrder.Insert(order, this);
// There are more efficient ways, but you get the idea.
}
public void SendBack() { SetZOrder(zOrder.indexOf(this) + 1); }
public void SendToFront() { SetZOrder(0); }
// etc...
}
Part Two
There are multiple ways to approach part two. The most obvious is to run a check against all interface components for intersection and layer property, or in the case of a Z-Ordered list, all components higher up the list (approaching 0 in my example) for intersection.
This can end up being pretty expensive, even if you use screens to make the list smaller. Instead you can manage a list of raised events and process them after you handle input. For example...
public static class InterfaceEvents
{
public static List<EventData> List = new List<EventData>();
public static void Resolve()
{
while (List.Count > 0)
{
for (int i = List.Count - 1; i > 0; i--)
{
if (List[i].EventType == List[0].EventType && List[i].Sender.Intersects(List[0].Sender))
{
if (List[i].Sender.Layer < List[0].Layer) // However you choose to manage it.
{
List[0] = List[i];
List.RemoveAt(i);
}
else
List.RemoveAt(i);
}
}
// Toggle event from List[0]
List.RemoveAt(0);
}
}
}
public struct EventData
{
public InterfaceComponent Sender;
public int EventType;
}
Anyway, those are my thoughts. It's pretty late at night, so I hope everything's remained sensible and there are no glaring mistakes.
Usually in GUI there is a list of visibility ordering (z-order) that maintains what is on top of what. Using this technique (assigning a z order to each of your component) you can check if there is anything more toward the top of a clicked component that also includes the clicked coordinates -- if there is, do not handle the click (som other component is on top, that will handle it); otherwise this component is the topmost one to handle the click
A simple solution is creating a list in your Game class:
List<XNAInterfaceObject> controls;
You can then use the order of the list for your problem. Think of the first element in your list as the control that is at the front. In the GetControlAt() method of your game, you can loop through the controls from front to back:
protected override void Update(GameTime gameTime)
{
...
MouseState ms = Mouse.GetState();
if (ms.LeftButton == ButtonState.Pressed)
{
XNAInterfaceObject control = GetControlAt(ms.X, ms.Y);
if (control != null)
control.MouseClickMethod(ms);
}
...
}
private XNAInterfaceObject GetControlAt(int x, int y)
{
for (int i = 0; i < controls.Count; i++)
{
if (controls[i].Rectangle.Contains(x, y)
{
return controls[i];
}
}
return null;
}
This means that a XNAInterfaceObject should have a Rectangle property and a MouseClickMethod(). Keep in mind that when drawing your controls, you have to loop through the list backwards.

Categories

Resources