Xamarin Forms Switch How To Remember & Update User Settings (Push Notifications) - c#

I am working on a project / settings page that has a toggle / switch within Xamarin Forms. I am using xamarin essentials for storing user data, etc.
The goal is to have the switch / toggle save the position of the toggle when a user makes a change. And have this bound / binding to the app settings. I already have push notifications installed however I want to give the user the option to unsubscribe or to subscribe again.
Based on OneSignals documentation here: https://documentation.onesignal.com/docs/xamarin-sdk#section--setsubscription-
This allows for the SetSubscription to be true or false. OneSignal.SetSubscription(false);
This is my SettingsPage.xaml
<Switch IsToggled="True" Toggled="OnToggled" />
This is my SettingsPage.xaml.cs
void OnToggled(object sender, ToggledEventArgs e)
{
// Perform an action after examining e.Value
}
My Goal is to have some app properties set and placed within the App.xaml.cs to be enabled on app launch and changed based on if the app contains the following keys to enable or disable notifications.
This is my App.xaml.cs
public App()
{
InitializeComponent();
if (Application.Current.Properties.ContainsKey("PushDisabled"))
{
//Do things when push is disabled...
OneSignal.SetSubscription(false);
Xamarin.Essentials.Preferences.Set("SetSubcription", false);
}
else
{
Application.Current.Properties["PushDisabled"] = false;
//Do things when push is enabled...
OneSignal.SetSubscription(true);
Xamarin.Essentials.Preferences.Set("SetSubcription", true);
}
OneSignal.Current.StartInit("one-signal-key").EndInit();
}
Note: OneSignal.SetSubscription(false); also causes this error: 'OneSignal' does not contain a definition for 'SetSubscription'.
Which is confusing, because it does reference it being here: https://documentation.onesignal.com/docs/xamarin-sdk#section--setsubscription-
I need help creating the proper code to actually set the key when a user has disabled notifications and to set it back to enabled when a user has clicked to toggle / switch to active again.
Any help or suggestions is appreciated the StackOverflow community has been very helpful for me reviewing other questions however could to find anything on this topic...

Since you are already using Xamarin Essentials for storing data. Try to store the toggled information in the Preferences and access it in your ViewModel to Bind to the UI. Refer to the below code.
public App()
{
InitializeComponent();
Xamarin.Essentials.Preferences.Set("SetSubcription", true);
OneSignal.SetSubscription(true);
}
Your view model must have a property accessing the value from the preferences.
public bool IsToggledValue
{
get { return Xamarin.Essentials.Preferences.Get("SetSubcription", false); }
}
Then you can bind the property from the ViewModel to your View in whichever page you are using it.
<Switch IsToggled="{Binding IsToggledValue}" Toggled="OnToggled" />
Please note that the BindingContext for your Switch must be ViewModel to get this logic working.
In your OnToggled callback, you can decide whether to subscribe to the push notifications or not.
private void OnToggled(object sender, ToggledEventArgs e)
{
if(e.Value)
{
OneSignal.SetSubscription(true);
}
else
{
OneSignal.SetSubscription(false);
}
}
Hope this helps you. Please let me know you need any further clarification.

OneSignal.Current.StartInit("app-id-code-here").EndInit();
if (!Application.Current.Properties.ContainsKey("PushDisabled"))
{
//Prepare notification for first time.
App.Current.Properties.Add("PushDisabled", false);
App.Current.SavePropertiesAsync();
Xamarin.Essentials.Preferences.Set("SetSubcription", true);
OneSignal.Current.SetSubscription(true);
}

Related

How to switch pages in the Xamarin Shell without a back button in the top navigation

I have picker in my left menu navigation shell that change the main page language,
for example actually the app in English. When I change to French, it change and redirect to main page with French text.
The problem is when I press the back arrow in navigation page, it back to old language (English).
Here the shell navigation
<Shell ..>
<Shell.FlyoutHeader>
<Picker ItemsSource="{Binding MultiLang, Mode=TwoWay}" SelectedItem="{Binding SelectedLang}">
</Picker>
</Shell.FlyoutHeader>
<ShellContent x:Name="home"
Route="main"
ContentTemplate="{DataTemplate home:Dashboard}" />
</Shell>
and this is the method that redirects to the main page:
private async void ChangeLange(string lang)
{
...
Routing.RegisterRoute(nameof(Dashboard), typeof(Dashboard));
await Shell.Current.GoToAsync($"{nameof(Dashboard)}");// it redirect but with button back
RemoveStackNavigation ()
// await Shell.Current.GoToAsync("//main"); like this , it dosent refresh the page with
Shell.Current.FlyoutIsPresented = false;
}
here is the MVMM
public string _selectedLang;
public string SelectedLang
{
get
{
return _selectedLang;
}
set
{
if (_selectedLang != value)
{
_selectedLang = value;
OnPropertyChanged("SelectedLang");
ChangeBuilding(value);
}
}
}
I tried to RemoveStackNavigation before make redirection to Dashboard like this :
public static void RemoveStackNavigation()
{
var existingPages = Shell.Current.Navigation.NavigationStack.ToList();
foreach (var page in existingPages)
{
if (page != null)
Shell.Current.Navigation.RemovePage(page);
}
}
I didn't understand so much of what happens, but I assume that you want to change the language of your app when you swipe the picker. I think the problem isn't in the navigation, but the way of pages are created.
For example, if you run now your app and it goes to a Main page it will stored in a temp cache, so if you go to another page and go back, it will load faster than the first time that you entered in app.
Something that you can test is (ONLY TEST):
Put this picker result (e.g: if is French than a variable is 1, if is English, 2, etc) and this variable as static, just to it don't refresh and you lost this value.
In your main page, create a Label and the set the dynamic text:
switch(pickerResult):
case "1":
lblTest.Text = "french";
break;
case "2":
lblTest.Text = "English";
break;
case "3":
lblTest.Text = "Italian";
break;
AND, the page that you go when you click in back button that now is showing in English, create exactly the same label above, and see if works.
If it works, then some hints:
Your page can be with static texts;
Your page isn't getting the language in runtime;
SO, to solve this, you need to create some List<Words> and get the properly language when the picker was changed:
private void SelectedIndexChanged(sender s, EventArgs e)
{
switch(pickerResult):
case "1":
App.wordsList = GetListOfWords("french");
break;
}
This GetListOfWords is a function to you call your xml or App Resource or whatever you're using to change the language (even if was a translator).
Now your wordsList (or whatever are your way to call it) will be updated.
The navigation stack of Shell has something special. Clear the navigation stack is not a good choice. We always use Routes to do the navigation for the Shell. You need to know which would generate the stack.
A route, which defines the path to content that exists as part of the Shell visual hierarchy.
A page. Pages that don't exist in the Shell visual hierarchy can be pushed onto the navigation stack from anywhere within a Shell application. For example, a details page won't be defined in the Shell visual hierarchy, but can be pushed onto the navigation stack as required.
For more details, please check the MS docs. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation
When you press the back button, override the back button event and then judge the route to do the navigation would be better. Use the specific route name to go back.
Update:
After compilation the Navigation Bar that we call in Xamarin Forms, turns into the Action Bar for Android during runtime.
Set the toolbar after LoadApplication(new App()); in OnCreate of MainActivity.
AndroidX.AppCompat.Widget.Toolbar toolbar
= this.FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
And then overrode OnOptionsItemSelected(). When you press the backbutton on navigation bar, this event would be triggered.
public override bool OnOptionsItemSelected(IMenuItem item)
{
}

Visual Studio/C# Issues with .Checked Boxes In User Settings

So I'm dabbling in C# properly for the first time trying to make a WPF based desktop application.
So far it's mostly going well, however as part of the project I am trying to take input from the user in one window (essentially where they define a project and the settings they want) and save them for later user in the project and have it act accordingly based on their input.
I've figured out the saving of this data for text inputs etc, however I'm having issues replicating this for check boxes.
I've defined the setting in the settings page as a bool defaulting to false, with the intent to be if the user ticks the checkbox then set the setting to true for later use.
When I'm trying to use .Checked against my checkbox class name it says it must appear on the left hand side of of += or -=, I've looked online for clarification and most similar code & relevant tutorials define it the way I have without issue.
Here's a snippet of the code:
public void CXML_GetSettings()
{
CXML_NewProject_Inc_SubModule_XML.Checked = Properties.Settings.Default.CXML_Project_Inc_SubModule;
}
Tried various ways of changing it but just can't get .Checked to work anywhere.
The Checked is an event for CheckBox, if you want to use it for your CheckBox, you can use it like below:
public MainWindow()
{
InitializeComponent();
MyCheckBox.Checked += MyCheckBox_Checked;
}
private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}
Or
<CheckBox IsChecked="False" Name="MyCheckBox" Checked="MyCheckBox_Checked"/>
private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}

What could be preventing my combobox dropdown from showing after app focus is lost?

I have a ComboBox in a WPF app that has recently been refactored to use the MVVM pattern. An apparent side effect to this change is that changing focus to another application while the combobox dropdown is visible completely prevents the dropdown from being visible again, until the app has been restarted.
The ComboBox DataContext is set to my ViewModel, with its ItemsSource bound to an ObservableCollection<String> SearchSuggestions, and IsDropdownOpen bound to a property SuggestionsVisible in the ViewModel.
The desired effect is a search box with autocomplete suggestions. It should close if there are no suggestions in the ObservableCollection, if the user cancels the search, if the user runs the search, or if the user clicks away from the text field - either inside the app or outside it.
The ViewModel explicitly sets the SuggestionsVisible property to true or false based on whether SearchSuggesions contains any items after user input. This process continues to take place after this bug manifests itself, just with no visible change to the UI. Any idea why losing focus while the dropdown is open renders the dropdown un-openable for the rest of the app's session?
Here's how I have things wired together:
<ComboBox DataContext="{Binding SearchBoxVm}" Name="cmboSearchField" Height="0.667"
VerticalAlignment="Top" IsEditable="True" StaysOpenOnEdit="True"
PreviewKeyUp="cmboSearchField_OnKeyUp"
PreviewMouseLeftButtonUp="cmboSearchField_OnPreviewMouseLeftButtonUp"
Background="White" ItemsSource="{Binding SearchTopics}"
IsDropDownOpen="{Binding SuggestionsVisible,
UpdateSourceTrigger=PropertyChanged}"
Margin="50.997,15.333,120.44,0"
RenderTransformOrigin="0.5,0.5" Grid.Row="1" >
<!-- SNIP STYLING -->
</ComboBox>
ViewModel:
public class SearchBoxViewModel : INotifyPropertyChanged
{
public void ResetSearchField(bool preserveContents = false)
{
if (!preserveContents || string.IsNullOrEmpty(Query))
{
Foreground = Brushes.Gray;
QueryFont = FontStyles.Italic;
Query = DEFAULT_TEXT;
}
}
public bool OnKeyUp(Key key)
{
bool showDropdown = SuggestionsVisible;
bool changeFocusToCombobox = false;
if (keyInValidRange(key))
{
SearchSuggestions = GetSearchSuggestions(Query);
if (SearchSuggestions.Count > 0)
{
SuggestionsVisible = true;
}
}
return changeFocusToCombobox;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
bool _suggestionsVisible = false;
public bool SuggestionsVisible
{
get { return _suggestionsVisible; }
set
{
// this section is still called after this issue manifests,
// but no visible change to the UI state is made
_suggestionsVisible = value;
NotifyPropertyChanged("SuggestionsVisible");
}
}
public ObservableCollection<String> SearchTopics = new ObservableCollection<String>();
}
The OnKeyUp() method is called by the MainWindow class ( haven't gotten as far as binding events to handlers specified in the ViewModel ), while but there's also a call to ResetSearechField from the MainWindow:
// Note: removing references to this event handler does not have any effect
// on the issue at hand... only including here for completeness
void window_Deactivated(object sender, EventArgs e)
{
SearchBoxVm.SuggestionsVisible = false;
SearchBoxVm.ResetSearchField(true);
}
I've spent quite a bit of time trying to debug this, and haven't seen any internal state changes that might account for this. The NotifyPropertyChanged event is otherwise behaving as it did before, and the stack trace window isn't showing any exceptions having been encountered.
Setting the binding mode on the IsDropdownOpen property to 'TwoWay' in the XAML hasn't had any effect either. Lastly, wrapping the assignment to SuggestionsVisible in a Dispatcher call on the main thread has had no effect on the issue either.
Any assistance would be appreciated.
#BrMcMullin, since you have stated that:
The desired effect is a search box with autocomplete suggestions.
may I ask, why do you choose to use standard ComboBox instead of specialized AutoCompleteBox that is available in the WPF Toolkit - February 2010 Release and seems like was especially designed for your case?
You may have noticed that first link points to documentation for its Silverlight predecessor, but don't worry - WPF Toolkit library include fully functional official WPF port of AutoCompleteBox from Silverlight. There is more info about this "event": AutoCompleteBox: Now with 100% more WPF.
With that control your auto complete popup could looks as simple as:
or as complex as:
So, if you will not manage to solve your issue with ComboBox's popup visibility, feel free to give a try to AutoCompleteBox. With it you could even leverage dynamic sorting of your suggestions if needed (just use answer from #adabyron).

Multiple frames of the same type Windows 8 c#

I have an application for Windows 8 with a page (Frame) for displaying a list of items and a page for downloading & displaying the items details. I am also using MVVM Light for sending notifications.
Application use goes something like this:
Open Main Page
Navigate to List Page
Frame.Navigate(typeof(MyPage));
Choose Item
//Complete logic
Frame.GoBack();
Back on Main Page, I start downloading the file in the view model, I send ONE NotificationMessage saying BeginDownloadFile and after it is downloaded ONE NotificationMessage saying EndDownloadFile.
The first time I do steps 2,3, & 4 my NotificationReceived method is hit once, the second twice and so forth.
private async void NotificationMessageReceived(NotificationMessage msg)
{
if (msg.Notification == Notifications.BeginDownloadFile)
{
FileDownloadPopup.IsOpen = true;
}
else if (msg.Notification == Notifications.EndDownloadFile)
{
FileDownloadPopup.IsOpen = false;
}
}
Additional information: I only have one FileDownloadPopup, yet each time, an additional popup is shown each time the NotificationMessageReceived method is called.
My only conclusion is that between navigating forwards and backwards in my app, there are multiple MainPages being created and never closed. This results in many NotificationsMessageReceived methods just waiting for a notification to come their way so they can show their popup.
I have two questions:
1. Does this sound like normal behaviour for a Windows 8 app?
2. How can I close all instances of the MainPage or return to the previous instance without creating a new instance?
Please let me know if I have missed something important out before marking my question down.
This sounds normal to me. The default navigation behaviour in Windows 8 is to create a new page instance each time you navigate to a new page, regardless of whether this is forward or back navigation.
Try setting the NavigatinCacheMode on MainPage to Required. See the MSDN documentation for details of how page caching works.
It sounds like you are registering eventhandlers in the page and then not removing them. Each time you navigate to the page again the handler is being added again in addition to the one you previously added. Try to add your event handler in OnNavigatedTo, and make sure you unregister it in OnNavigatedFrom.
protected override void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
MyEvent.OnDownloadRequest += MyLocalDOwnloadHandler; // add the handler
}
protected override void OnNavigatedFrom(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
MyEvent.OnDownloadRequest -= MyLocalDOwnloadHandler; // remove the handler
}

Quit Application when all forms is closed

I want to have a lot of forms in my Gtk# application. I want to quit application when user close all form. I try to use next code:
protected void OnDeleteEvent (object sender, DeleteEventArgs a)
{
/*Application.Quit ();
a.RetVal = true;*/
if(System.Windows.Forms.Application.OpenForms.Count==0)
{
Gtk.Application.Quit ();
a.RetVal = true;
}
}
But System.Windows.Forms.Application.OpenForms.Count allways return "0" regardless of the number of open forms (OS Ubuntu 12.04). How can I solve this problem and get actual quantity of open forms?
Thanks in advance
also tried to find an answer to that issue.
My current implementation is based on some concept I have seen in MS Visual Studio documentation:
in the project's main class maintain a static list of open windows.
do not use the delete event but the destroy event of the GTK# window (with the OnDelete event handler something did not work, if I remember correctly the windows delete cannot be directly called by a member function).
In the OnDestoy event handler: when the window gets destroyed then remove it from the static list. Then check the list for being empty, then quit the application.
Not sure if this is really ideal for GTK# windows but in my application this concept works.
regards
Harald

Categories

Resources