Repeated (inifinte) Task in c# MAUI - c#

This is about .Net6 and MAUI
I do have a List<> of items. Every x seconds in an infinitive loop I send a request with the *item *as key.
With the received message I want to refresh the content of my page.
The page is a normal ContentPage.
I am still learning, so I do not want to know, what a be better solutions might be (except if what I think is not possible).
I do not have much at the moment except these methods in my MainPage.xaml.cs
public partial class MainPage : ContentPage
{
int count = 0;
string previousitem = "";
Caps cap = new Caps(#"c:\tmp\xmldata.xml");
public MainPage()
{
InitializeComponent();
// Methods also in Class MainPage
GetNextItem(cap.ReturnListOfItems());
FillMaui();
}
Well, I tried so many things with Task, AutoResetEvent and TimeSpan and Timer, that I do not have any running code anymore. Well, except the code above...
I am really stuck now and I'm hoping for some hints.
GetNextItem() loops through all the items, starts again at the beginning when reaching the end of the list and even handles a changing list of items.
FillMaui() just changes atributes of my elements in the MainPage.xaml, for example: this.ItemName.Text = this.previousitem;
So, how do I start this inifinitive loop, without blocking my app?

You could use a timer and fire the refresh code once per the period you define, rather than having it run continuously which will block your app.

First method, you can use the RefreshView to provide the refresh command. RefreshView is a container control that provides pull to refresh functionality for scrollable content. But the child of a RefreshView must be a scrollable control, such as ScrollView, CollectionView, or ListView.
Second method, you can use the MVVM way to make the List change the item automaticly. I prefer you to use the ObservableCollection<T> instead of using the List You can check the From Data Bindings to MVVM.

Related

Winforms Adding and remove UserControls

So I have an application that uses a single master form with a menu along the left-side of the form.
On the right-side I have a panel that acts as a placeholder for any UserControl I have created.
Each time a user clicks on a menu item, the related UserControl will display using code like this:
//Display UserControl first
ucMyUserControl uc = new ucMyUserControl ();
uc.Dock = DockStyle.Fill;
pnlContainer.Controls.Add(uc);
Is there a better way than to just copy and paste this for each button or whenever I want to display a UserControl. Should I be creating a function to call each time and just passing the name of the UserControl I want to use?
Maybe I've got it all wrong in the first place with the way I'm using it - I'm new to this concept and just trying things out.
Sure, don't repeat yourself. Write DRY code with a helper method. It could look like this:
public void ShowPage(UserControl uc) {
while (pnlContainer.Controls.Count > 0) pnlContainer.Controls[0].Dispose();
uc.Dock = DockStyle.Fill;
pnlContainer.Controls.Add(uc);
}
And now you simply call ShowPage(new ucMyUserControl());
Note that the quirky looking while-loop is important, you don't just want to use the Controls.Remove() method. Disposing controls that you remove is very important, if you don't then they'll live forever and ultimately crash your program.

Navigation within Page using Template10

I have problem how to implement sub-page navigation in UWP. The page is in RootFrame, which I can use on navigation. But i want to use something like this:
<Page>
<Grid>
<Frame x:Name="MyFrame"/>
</Grid>
</Page>
What I want is, use Navigate method of control MyFrame in ViewModel. I can call the method from code-behind, but I'm developing my app using MVVM. I'm not sure, if Template10 can work with sub-frames.
I appreciate any advice.
EDIT:
More details:
I have pivot control which is in page. the pivot has 2 tabs (pivotitems). The content of the pivotitem must be navigable. What I mean: I pivotitem 1, I need to have one Frame and use it for navigation in the pivotitem. My problem is, how to use or how to call the frame in pivotitem from ViewModel, especially I need to call Navigate method. Now I'm using Template10's navigation service and it's working with rootframe. I don't know, how to use it for other let's say sub-frames.
You can always do this.
var nav = Bootstrapper.NavigationServiceFactory(BackButton.Attach, ExistingContent.Exclude, this.Frame);
This will give you a navigation service for the frame in your page. You can then use session state, if you like.
Bootstapper.SessionState["MyNav"] = nav;
From here your view-model can access the service and navigate. You can repeat this for as many frames as you have. And you can then handle navigation in your view-model without consideration of "where" the frame is, just that your logic requires it to nav.
Does this make sense?
I don't know how you are going to trigger the navigation change so I'll assume it will start from a button click. I am also assuming the button's Command property is already bound to an ICommand in the viewmodel (the same concepts can be applied to different kinds of views).
All we have to do now is to make the ICommand implementation call our custom NavigationService to perform the content switch. This NavigationService class will be nothing but a simple proxy to the window global frame. Its main navigation method can be as simples as:
public void Switch()
{
var rootFrame = Window.Current.Content as Frame;
if ((rootFrame.Content as ParentPage) != null)
{
rootFrame.Navigate(typeof(ChildPage));
}
}
So you have tagged this with Template10 but it seems to be a more general question for UWP as a whole. I wonder if you have considered all of the inherent complexities with this approach - specifically related to suspension and resume. For each frame you have, you would need to save and restore navigation state, which isn't straight-forward when you have nested frames. Have you also considered how global navigation would work?
Template 10 does support the concept of multiple NavigationServices and, therefore, multiple frames, but only from the perspective of you can create them. Template10 does not inherently understand how such frames may be related to each other, so cannot perform automatic back propagation where you have something like:
FrameA[Main->Page1->Page1:Pivot1.FrameB[View1->View2->View3]]
Here we have two frames - FrameA and FrameB. FrameA has navigated from
Main to Page1. Page1 has a Pivot that hosts FrameB in PivotItem1 and
FrameB has navigated from View1 to View 2 and from View2 to View 3.
Global navigation (i.e. the shell back, etc.) would be automatically wired to FrameA, so you would need to intercept that action, and then handle you own navigation activity for FrameB.
Take a look at the BackButtonBehavior to see how it is possible to intercept the global back and then put in place your own action.
I don't know if you can do something like that..
One possible workaround is to use a Messenger that sends a message from your viewmodel to the view's code behind.. I'm not a fan of this solution though, because as I said before you have to use the page's code behind..

ListBox.ScrollIntoView works only after delay

I have a problem with a listbox.ScrollIntoView method - it does not work. Here is the code snippet:
// the listbox is binded to a "Thumbnails" property
this.Thumbnails = new VirtualizableCollection<RecordingThumbnailItem>(this.thumbnailsProvider) { ItemsStep = this.ThumbnailsStep };
this.listBox.ScrollIntoView(this.Thumbnails[thumbnailToSelect]);
I've noticed that if I call ScrollIntoView a little bit later (for instance in 500 milliseconds after the source for a binding was defined) everything works. So I suppose that ScrollIntoView should be invoked after the control will obtain some specific state;
if so, how can I detect it? Maybe using some event?
Eventually, I just need to enforce my horizontal listbox to show the last item in a right end, but not in a left as usual. Maybe some other approach exists?
The problem is the views that represent each of the items hasn't been created yet, so the view can't be scrolled onto the screen.
You can use the Dispatcher to queue up the ScrollIntoView() call with a lower priority than the UI, which gives the UI time to generate the views.
Try this:
this.Thumbnails = new VirtualizableCollection<RecordingThumbnailItem>(this.thumbnailsProvider) { ItemsStep = this.ThumbnailsStep };
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(() => this.listBox.ScrollIntoView(this.Thumbnails[thumbnailToSelect])
);
You might need to substitute Dispatcher.CurrentDispatcher with Application.Current.Dispatcher if the CurrentDispatcher happens to be one other than the UI one.

How can I write a BringToFront method for a WPF UserControl?

I am developing a UserControl, call it CoolControl, that is meant to act somewhat like a window, with a few special features. So far, it can be resized and dragged all around the screen. If I add multiple CoolControl objects to my application window using XAML, the last one that was declared is always in front. This is fine, but I want to make it so that if I click on one of my CoolControl objects during run-time, that control will put itself in front of all the other controls.
I've tried using Canvas.SetZIndex, but unless I'm simply unable to come up with a clever enough solution, I don't see how that can help me. Because once I set one control's Z-Index to 9999, over time every other control I click will have the same value of 9999. And then, once again, the control declared last ends up in front.
If you were given the task of writing a BringToFront() method for someone's UserControl, how would you do it in the simplest way possible? I'd prefer a better solution than getting the parent window, looping through all the controls, finding the maximum Z-Index, and then setting the Z-Index of the CoolControl accordingly, if THAT is even a valid solution.
I'm not familiar with the Canvas.SetZIndex method. It looks like some sort of attached property or behaviour.
If you can provide the logic to set the z-index, I've outlined a way to keep track of the instances and manage the z-indexes, keeping them in the order in which they have been selected/created.
public class CoolControl : UserControl
{
public CoolControl()
{
InitializeComponent();
Instances.Add(this);
}
static IList<CoolControl> Instances = new List<CoolControl>();
void SelectThisInstance()
{
foreach(var instance in Instances)
{
// decrement z-index instance
}
// set z-index of this instance to show at top
}
}

Bring Winforms control to front

Are there any other methods of bringing a control to the front other than control.BringToFront()?
I have series of labels on a user control and when I try to bring one of them to front it is not working. I have even looped through all the controls and sent them all the back except for the one I am interested in and it doesn't change a thing.
Here is the method where a label is added to the user control
private void AddUserLabel()
{
var field = new UserLabel();
userContainer.Controls.Add(field);
SendLabelsToBack(); // Send All labels to back
userContainer.Controls[field.FieldName].BringToFront();
}
Here is the method that sends all of them to the back.
private void SendLabelsToBack()
{
foreach (var label in userContainer.Controls);
label.SendToBack();
}
Yeah, there's another way. The Controls.SetChildIndex() also changes Z-order. The one with index 0 is the one on top. Doesn't buy you anything though, BringToFront() uses this method.
Your SendLabelsToBack() method as given cannot work, it will also send the label to added to the back. But your next statement fixes that again.
Okay, that doesn't work, which means the BringToFront() method doesn't get executed. Look in the Output window for a "first chance exception" notification. As written, your SendLabelsToBack() will cause an exception if the user control contains any control other than a UserLabel. Also, set a breakpoint after the BringToFront() call and check the value of userContainer.Controls[0].Name when it breaks.
Controls' z-index is per-container.
If you call BringToFront on a control that is inside a container (such as a Panel), it will not bring the container to the front.
Therefore, the control will only go in front of other controls in that container.
To see what containers your controls are in, you can use the Document Outline pane in the View menu.
EDIT: Your userContainer control is probably behind a different control.
Have you tried Invalidate() after BringToFront()? BringToFront does not raise the Paint event
try this:
private void SendLabelsToBack()
{
foreach (var label in userContainer.Controls)
{
label.SendToBack();
label.Invalidate();
}
}
I think you just need to change your last line:
userContainer.Controls[field.FieldName].BringToFront();
to this:
userContainer.Controls[field.Name].BringToFront();
When you use a string as the indexer for the Controls collection, it goes by the Name property of the control (not the FieldName property).
Since you're just trying to bring the most recently-added control to the top, this would also work:
userContainer.Controls[userContainer.Controls.Count - 1].BringToFront();
From my experience looks like windows puts all controls belonging to one graphic container(pane, group box...etc) in a software collection. The collection is ordered by child index which is a property of every control in that container.
The trick is that children with the same index can and do exists. In this case windows will paint those children ordered relative to others but between them it will paint them in the reverse order they had been added to the container.
Long story short: for one container-you need to make sure controls have different indexes by changing ALL NOT just SOME of the indexes when you want to change the z-order. For example:
foreach (Control newControl in TopControl.Controls)
{
TopControl.Controls.SetChildIndex(newControl,indexlogic(newControl));
}
where indexLogic(newControl ) is your method of calculation of the index of particular control.

Categories