How do I make a ProgressBar with rounded corners using Xamarin Forms - xamarin

So my goal is to create a ProgressBar with rounded corners. This seems to be way harder than what it should be. I've tried looking at examples on StackOverflow but most of them are outdated or is referring to Android Studio which is NOT what I am working with.
As far as I know, I need to create a whats called "Custom Renderer" which is essentially a class that inherits from ProgressBarRenderer. And then change most of the properties there, which seems kinda silly since the UI should be done using XAML in my opinion.
I keep running into multiple issues when trying to create the custom renderer.
The first one being this
I've already installed Xamarin.Forms and it still keeps throwing that error.
And then there is a issue regarding the constructor. The constructor that doesn't take any parameters is obsolete and doesn't work anymore and it requires me to add a Context which I can't either because it throws the same error as above.
How do I properly create a progressbar with rounded corners and text in the middle indicating how much % it's at?

You should add proper reference to use the ProgressBarRenderer:
using App498.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
The custom renderer should look like this:
[assembly: ExportRenderer(typeof(Xamarin.Forms.ProgressBar), typeof(myProgressBarRenderer))]
namespace App498.Droid
{
public class myProgressBarRenderer : ProgressBarRenderer {
public myProgressBarRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ProgressBar> e)
{
base.OnElementChanged(e);
}
}
}
If you want to add round corner to progress bar, check this thread.

I would refer you to SkiaSharp, graphics library, that enables you to create... well everything.
See the documentation here:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/
You'll need to play around for a while to get used to work with it, but it's quite intuitive and easy to work with.
To achieve round corners, you'll need to work with paths, described in here:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/clipping
By a chance I also needed a progress bar in my app. I've drawn a picture with transparent line within for the progress to be shown, merged it with green rectangle (the progress itself) and voila, you've got the progress bar. When you need to update the progress, you just redraw the progress bar.
There are surely plenty of other solutions, but this one I reccomend as I know it will work fine.
Hapyy coding ;)

Related

Unity UI not working properly ONLY on Windows

I have been working on figuring out what is going on with my game's UI for at least two days now, and no progress.
Note that this is a mobile game, but I was asked to build for Windows for visualization and presentation purpose.
So the problem is that when I run my game on the Unity Editor, Android, iOS and Mac platforms the UI works just perfect, but then when I run the game on Windows the UI still works fine UNTIL I load a specific scene.
This specific scene is a loading screen (between main menu and a level) when the level finished async loading, a method called MoveObjects is called in a script in the loading screen, to move some objects that where spawned in the loading screen scene into the level scene (this is not the issue though, since I already try without this method and the problem on the UI persist).
Once the logic of this MoveObjects method is done, a start button is enabled in the loading screen, for the player to click and start playing (I did try moving the start button to the level scene, since maybe it not been a child of the currently active scene could be the issue, but the problem still persist). Is at this point that the UI is partially broken, what I mean with this is, that I can see buttons (and some other UI elements like a scrollbar) changing color/state when the mouse moves over them, but I cannot click on them anymore (the button wont even change to the pressed state).
Also note that I tried creating a development build to see if there was any errors in the console, and I notice that this problem is also affecting the old UI system, so I was not able to interact with the development console anymore.
Also also, note that if I grab and drag the scrollbar before this issue appear, and I keep holding down on the scrollbar until this happens, the mouse gets stuck on the scrollbar, meaning that I cannot interact with the UI anymore, but the scrollbar will still move with the mouse.
I already check that this things are not the source of the problem:
Missing EventSystem, GraphicRaycaster or InputModule.
Another UI element blocking the rest of the UI.
Canvas is Screen Space - Overlay so there is no need for a camera reference.
I only have one EventSystem.
Time.timeScale is 1.
I am not sure what else I could try, so if anyone has any suggestions, I would appreciate it. Thanks.
P.S: I am sorry to say that I cannot share any code or visual material or examples due to the confidentiality.
A major source for a non-working UI for me has always been another (invisible) UI object blocking the raycast (a transparent Image, or a large Text object with raycast on).
Here's a snippet I put together based on info found elsewhere, I often use it to track objects that are masking the raycast in complex UI situations. Place the component on a text object, make sure it's at least few lines tall, as the results will be displayed one under another.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
[RequireComponent(typeof(Text))]
public class DebugShowUnderCursor : MonoBehaviour
{
Text text;
EventSystem eventSystem;
List<RaycastResult> list;
void Start()
{
eventSystem = EventSystem.current;
text = GetComponent<Text>();
text.raycastTarget=false;
}
public List<RaycastResult> RaycastMouse(){
PointerEventData pointerData = new PointerEventData (EventSystem.current) { pointerId = -1, };
pointerData.position = Input.mousePosition;
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
return results;
}
void Update()
{
list= RaycastMouse();
string objects="";
foreach ( RaycastResult result in list)
objects+=result.gameObject.name+"\n";
text.text = objects;
}
}

How to go back more than one view model in mvvm light

I am doing xamarin development and I am not doing forms.
I want to go back by 3 viewcontrollers/activities but goback method is for going back by one viewcontroller or activity
If I use navigateto i believe one more time the viewcontrolller/activity gets added.i.e two instances.
So how to solve this problem ?
Update:
Here is the inavigation interface there is no way to access thr navigational stack as well
If you are going back, then use INavigationService.GoBack();. You can call that 3 times in a row to go back 3 pages.
private void GoBackThree()
{
_navigationService.GoBack();
_navigationService.GoBack();
_navigationService.GoBack();
}
I haven't used MVVM Light before but I have quite a bit of experience of Xamarin Forms. I used to use MVVM Cross for navigation but eventually found that the built in Xamarin Forms Navigation was much better, I ran into similar scenarios as to what you have here.
Under the hood it looks like MVVM Light is just wrapping the Xamarin Forms Navigation anyway - http://mvvmlight.codeplex.com/SourceControl/latest#Samples/Flowers/Flowers.Forms/Flowers.Forms/Helpers/NavigationService.cs
I don't think you need give up on MVVM Light navigation but you do need to get under the hood to achieve the navigation without seeing three transitions. I haven't tried this with MVVM Light but it works great with normal Forms navigation.
Step 1
Get access to the underlying Xamarin Forms navigation:
var navigation = Application.Current.MainPage.Navigation;
Step 2
Remove the two pages you want to skip when navigating back. It's important that you remove them before navigating backwards otherwise you'll get a double transition (note the -2 is because you want to remove the second to last page).
var firstPageToRemove = navigation.NavigationStack[navigation.NavigationStack.Count - 2];
navigation.RemovePage(firstPageToRemove);
var secondPageToRemove = navigation.NavigationStack[navigation.NavigationStack.Count - 2];
navigation.RemovePage(secondPageToRemove);
Step 3
Navigate backwards
_navigationService.GoBack();
I hope that works for you.
Alternative Consideration
I have a similar application in my app which I've solved a little more elegantly. If you know you never need to be able to navigate back to those previous pages. When you push the new pages on, you can actually remove the previous one if you no longer need it. Please note you have to remove the page after you push on the new one otherwise you get two transitions. I use this extension method on-top of the standard Forms Navigation which I showed you how to access in Step 1 to achieve it.
public static async Task ReplaceCurrentAsync(this INavigation navigation, Page page, bool animated = false)
{
var curentPage = navigation.NavigationStack.Last();
await navigation.PushAsync(page, animated);
navigation.RemovePage(curentPage);
}

Mono navigation

I just started out with mono and I've already gotten problems. I'm used to play with c# code and was told that mono would be easy for me, but no no. I simply want to start a new activity and close the one i just were using. I checked out some mono API examples, but they are simply too complicated for this task. It has to be some easier way of doing it. This is my first activity class:
[Activity(Label = "CryptotoDroid", MainLauncher = true, Icon = "#drawable/icon")]
public class Activity1 : Activity
{
EditText inputpassword;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
Button button = FindViewById<Button>(Resource.Id.MyButton);
inputpassword = FindViewById<EditText>(Resource.Id.beforetext);
button.Click += delegate
{
if (inputpassword.Text == "Moo")
{
StartActivity(typeof(ActivityContacts));
}
};
}
}
This is what i tried, but the program crashes. I Simply want to make the program to start a new activity when the password is "moo".
The activity I want to start is:
[Activity(Label = "My Activity")]
public class ActivityContacts : Activity
{
protected override void OnCreate(Bundle bundle)
{
SetContentView(Resource.Layout.Main);
var contactgrid = FindViewById<GridView>(Resource.Id.gridview);
}
}
Later on, i would also like to fill out my gridview with all contacts in the phone, but that belongs to another topic.
Did you mean to set both activities' content views to the Main layout?
SetContentView(Resource.Layout.Main);
public void setContentView (View view)
Set the activity content to an explicit view. This view is placed directly into the activity's view hierarchy. It can itself be a complex view hierarchy. When calling this method, the layout parameters of the specified view are ignored. Both the width and the height of the view are set by default to MATCH_PARENT. To use your own layout parameters, invoke setContentView(android.view.View, android.view.ViewGroup.LayoutParams) instead.
For starting with MonoDroid, I recommend:
getting hold of one of the excellent books out there (Wally, Greg, Chris or others will be along with suggestions - I've personally yet to see a bad one so won't give a recommendation!)
try watching a couple of the Xaminar's on youTube -http://www.yourepeat.com/g/Xaminar
try building the Xamarin sample programs and then try adapting them - it's sometimes easier to adjust working programs than it is to start new ones (sometimes!)
try building new things just as you have above.
When you hit problems - as we all do - then please do ask questions here or on the Xamarin forums - people will help.
However, when you hit a crash or exception, please try:
Give us as much info as you can about the crash/exception - including there are some ways to get extra debug logs off of a phone or emulator - http://docs.xamarin.com/Android/Guides/Deployment,_Testing,_and_Metrics/Android_Debug_Log - these logs often contain important text which will help us help you diagnose the crash
If you are running under the VS2010, VS2012 or MonoDevelop debugging tools, try adding extra Console.WriteLine statements and/or use breakpoints - this can help you and us work out which line is causing the crash - or if the crash is occurring somewhere in setup before the code even runs.
Speaking personally, I believe Mono for Android does help experience C# devs exploit their skills on Android - but there's still new things to learn, and there are still embedded development frustrations to tackle and overcome (like these sort of crashes)

Big problems with MFC/WinAPI

I need to create a SDI form with a formview that has two tabs, which encapsulate multiple dialogs as the tab content. But the form has to have a colored background.
And things like these makes me hate programming.
First, I tried CTabControl, via resource editor, tried different things, but the undocumented behavior and the quirks with no answers led me into a roadblock.
After many hours of searching, I found that there is a control called property sheet, which is actually what I need.
Some more searching later, I found that property sheet can even be actually embedded onto CFormView like so: http://www.codeguru.com/Cpp/controls/propertysheet/article.php/c591
And that the dialog classes derived from CPropertyPage can be directly added as pages via AddPage method of CPropertySheet.
Great! Not quite so... Some of the controls didn't worked, and were not created, ran into weird asserts. Turns out the DS_CONTROL style was missing from the dialogs. Found it completely accidentaly on Link, no word about that on MSDN!!!! Property page must have: DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_TABSTOP, and can have: DS_SHELLFONT | DS_LOCALEDIT | WS_CLIPCHILDREN styles! Not any other, which are created by default with resource editor. Sweet, super hidden information for software developers!
The quote in comments on that page: "OMG. That's where that behavior came from...
It turns out that the PlaySound API relied on that behavior when playing sounds on 64bit machines." by Larry Osterman, who as I understand works for Microsoft for 20 years, got me laughing out loud.
Anyway, fixed that, the dialog-controls(CPropertyPages) are created as expected now, and that part looks something remotely promising, but the next part with color is dead end again!
Normally you override WM_CTLCOLOR, check for control ID or hwnd and supply the necessary brush to set the color you need. Not quite so with CPropertySheet, the whole top row stays gray! For CTabCtrl it somehow works, for CPropertySheet it doesn't.
Why? Seems that the CPropertySheet is kinda embedded inside CTabControl or something, because if I override WM_ERASEBKGND, only the internal part changes the color.
Now it seems that there is a GetTabControl() method in the CPropertySheet, that returns the actual CTabCtrl* of the CPropertySheet. But since it's constructed internally, I can't find how to override it's WM_CTLCOLOR message processing.
There seems to be a way to subclass the windowproc, but after multiple tries I can't find any good source on how to do it. SubclassWindow doc on MSDN says: "The window must not already be attached to an MFC object when this function is called."?! What's that?
I tried creating a custom CCustomTabCtrl class based on CTabCtrl via MFC wizard, created an instance of it, called SubclassWindow from one of the CCustomPropertySheet handlers to override the internal CTabCtrl, but nothing works, mystical crashes deep inside MFC.
Tried setting WindowLong with GCL_HBRBACKGROUND for the internal CTabCtrl, nothing changed.
And worst of all, I can't find any sort of useful documentation or tutorials on the topic.
Most I can find is how to ownerdraw the tab control, but this is seriously wrong on so many ways, I want a standard control behavior minus background color, I don't want to support different color schemes, windows versions, IAccesible interfaces and all this stuff, and none of the ownerdraw samples I've seen can get even 10% of all the standard control behavior right. I have no illusion that I will create something better, I wont with the resources at hand.
I stumbled upon this thread, and I can't agree with the author more: http://arstechnica.com/civis/viewtopic.php?f=20&t=169886&sid=aad002424e80121e514548d428cf09c6 owner draw controls are undocumented PITA, that are impossible to do right, and there is NULL information on MSDN to help.
So is there anything I have missed or haven't tried yet? How to change the top strip background color of the CPropertySheet? Anyone?
Your only option is to ownerdraw the tab control. It's not that hard. Well, it is frustrating because MFC doesn't tell you how to make the necessary Win32 calls.
In your CPropertySheet-derived class, overwrite OnInitDialog() and add:
GetTabControl()->ModifyStyle(0,TCS_OWNERDRAWFIXED);
This puts your CPropertySheet-derived class in charge of drawing the tab control. Add a handler for WM_DRAWITEM (OnDrawItem) and change backgroundColor and textColor to match whatever colors you wanted. Code for OnDrawItem follows:
void CPropSht::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (ODT_TAB != lpDrawItemStruct->CtlType)
{
CPropertySheet::OnDrawItem(nIDCtl, lpDrawItemStruct);
return;
}
// prepare to draw the tab control
COLORREF backgroundColor = RGB(0,255,0);
COLORREF textColor = RGB(0,0,255);
CTabCtrl *c_Tab = GetTabControl();
// Get the current tab item text.
TCHAR buffer[256] = {0};
TC_ITEM tcItem;
tcItem.pszText = buffer;
tcItem.cchTextMax = 256;
tcItem.mask = TCIF_TEXT;
if (!c_Tab->GetItem(c_Tab->GetCurSel(), &tcItem )) return;
// draw it
CDC aDC;
aDC.Attach(lpDrawItemStruct->hDC);
int nSavedDC = aDC.SaveDC();
CBrush newBrush;
newBrush.CreateSolidBrush(backgroundColor);
aDC.SelectObject(&newBrush);
aDC.FillRect(&lpDrawItemStruct->rcItem, &newBrush);
aDC.SetBkMode(TRANSPARENT);
aDC.SetTextColor(textColor);
aDC.DrawText(tcItem.pszText, &lpDrawItemStruct->rcItem, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
aDC.RestoreDC(nSavedDC);
aDC.Detach();
}
Thank you for this solution but...
The above solution works well with one tab, but when you have multiple tabs it seems rename the wrong tabs. I needed to change the if statement for GetItem to:
if (!c_Tab->GetItem(lpDrawItemStruct->itemID, &tcItem )) return;
Needed lpDrawItemStruct->itemID to get the tabs named correctly

prevent blend from adding a startupURI to application?

Is there some trick that is known to tell blend to stop trying to set a startupURI in my app.xaml? I googled but didn't see anything so I figured I would ask here.
I use a startup routine and instantiate mainwindow myself. Every once and a while blend likes to toss in the startupURI="MainWindow.xaml" when I let it compile. Occasionally I see some message along of the lines of "There is no startup scene associated with this project. Would you like blend to blah blah fix it?" or something along those lines. I click cancel/no yet it still tosses a gremlin in my code. Internal to blend there is some mechanism for checking for this property or it wouldn't complain via dialog box to me. So how do I just tell it "no thanks blend, i'm good without that?", lol.
Its quite annoying. I open blend to do something simple like using a color picker and use it to compile because VS2010 isn't open. My result is two mainwindows. But it does not do it every time so it's not a repeatable behavior. The compiler just acts out randomly.
edit: I'm using blend 4 but I saw this happen when i was using blend 3 also.
This is a horrible, terrible hack, but hey, it works. By default, StartupUri is null, but you can't set it to null using the property, so you can go around the property if you like to live on the edge.
// Dangit blend! Stop inserting a stupid StartupUri
private void FixStartupUri()
{
var type = typeof(Application);
var startupUri = type.GetField("_startupUri", BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance);
startupUri.SetValue(this, null);
}
Add this to your Application class and call it like so:
protected override void OnStartup(StartupEventArgs e)
{
FixStartupUri();
base.OnStartup(e);
// Do the rest of your startup stuff.
}

Resources