In my app (C# + WinUI + Template Studio), I have an elaboration page and a left side Navigation Menu Items.
During the elaboration, I don't want that user can navigate to other pages.
What is the correct method to prevent this ?
I cannot find any example code to disable - temporarily - navigation.
I'm only capable to disable "back button" with:
Frame.BackStack.Clear();
I've tried to use App Service, like:
App.GetService<NavigationViewItem>().SelectsOnInvoked = false;
And also numerous variations, but without success, or looking for a "cancel event" when fired the:
private void OnNavigated(object sender, NavigationEventArgs e)
on "ShellViewModel", but cannot find it.
Thanks in advance for any suggestion.
You can just disable the NavigationView like this.
NavigationViewSerivice.cs
private async Task DoElaboration()
{
if (_navigationView is not null)
{
_navigationView.IsEnabled = false;
await Task.Delay(1000);
_navigationView.IsEnabled = true;
}
}
private async void OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
_navigationService.NavigateTo(typeof(SettingsViewModel).FullName!);
}
else
{
await DoElaboration();
var selectedItem = args.InvokedItemContainer as NavigationViewItem;
if (selectedItem?.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)
{
_navigationService.NavigateTo(pageKey);
}
}
}
Related
public static async Task SetRequestedThemeAsync()
{
foreach (var view in CoreApplication.Views)
{
await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
if (Window.Current.Content is FrameworkElement frameworkElement)
{
frameworkElement.RequestedTheme = Theme;
}
});
}
}
This is my code that correctly changes the theme of views in my app, but now I have some ContentDialogs and they don't change their theme.
So in this case I wanna ask two questions:
I content my popups in static full properties. Is this a good decision or will be better to create a new obj of popup every time?
If static properties are a good decision, how to change themes in them?
ContentDialogs and they don't change their theme.
It's looks known issue here. please feel free vote up this report. and currently there is a workaround that set the dialog's theme when show popup like the following.
private async void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
ContentDialog dialog = new ContentDialog();
dialog.Title = "Save your work?";
dialog.PrimaryButtonText = "Save";
dialog.SecondaryButtonText = "Don't Save";
dialog.CloseButtonText = "Cancel";
dialog.DefaultButton = ContentDialogButton.Primary;
dialog.Content = "Test Theme";
dialog.RequestedTheme = (Window.Current.Content as FrameworkElement).RequestedTheme;
var result = await dialog.ShowAsync();
}
The application is a machine control, so it needs access to ui to show status etc. (I know, goes against the recommendation to separate UI and work code, but it is what it is, at least for now). The issue boils down to this: When one button event handler is not finished, another button needs to be clicked twice. First click gives the focus to the button, next click fires the event.
Here is the issue simplified to extreme. There are two buttons and a label. Stop button needs two clicks to stop the machine:
bool Stop = true;
private void Start_button_Click(object sender, EventArgs e)
{
RunMachine();
}
private void Stop_button_Click(object sender, EventArgs e)
{
Stop = true;
}
private void RunMachine()
{
Stop = false;
Status_label.Text = "Running";
do
{
Application.DoEvents();
Thread.Sleep(50);
}
while (!Stop);
Status_label.Text = "Stopped";
}
How can I make the button to react to the first click?
DoEvents() is bad. Don't use it.
If you have to use it (e.g. as workaround), then you are adding technical debt and likely to pay in the future, similar to your case.
A better approach is to run work inside the task and use cancellation token, but in your case the minimum modification required is this (add async modifier to a method):
while (!Stop)
{
await Task.Delay(50);
// or
await Task.Run(() => Thread.Sleep(50));
}
The UI should be responsive now.
The latter is simulating synchronous code, put it instead of Sleep, don't forget to invoke if there you have to modify UI.
Thank you! I wasn't aware of the implications of Doevents, and using async and await is just as simple. I added a counter to show myself that the toy example is doing what I think it is. To make the answer complete and to help other noobs like me that might search answers for the same issue, here is the full example again. This works as wanted (stops with one click) and doesn't leave the RunMachine() running if the main form is closed without clicking stop. (My real application has enough code in the form closing event to prevent that, but I certainly wasn't aware of the trap.)
bool Stop = true;
private async void Start_button_Click(object sender, EventArgs e)
{
await RunMachine();
}
private void Stop_button_Click(object sender, EventArgs e)
{
Stop = true;
}
internal async Task RunMachine()
{
Status_label.Text = "started";
Stop = false;
int i=0;
do
{
await Task.Delay(500);
Status_label.Text = i.ToString();
i++;
} while (!Stop);
Status_label.Text = "Stopped";
}
In my windows phone 8.1 universal app project I am trying to make a share option.
But when I click on the button (ShareCommand) the Share UI is not showing up, I have tried this in the emulator and on a device.
The event is correctly wired up since the DataRequested event gets called, but after this event there is no Share UI showing.
Here is the code I use in my ViewModel (using prism framework).
private DataTransferManager _dataTransferManager;
private DelegateCommand _shareCommand;
// Share button
public DelegateCommand ShareCommand
{
get
{
return _shareCommand ?? (_shareCommand = new DelegateCommand(() =>
{
DataTransferManager.ShowShareUI();
}));
}
}
public override async void OnNavigatedTo(object navigationParameter, NavigationMode navigationMode, Dictionary<string, object> viewModelState)
{
base.OnNavigatedTo(navigationParameter, navigationMode, viewModelState);
// get data transfer manager and register events
_dataTransferManager = DataTransferManager.GetForCurrentView();
_dataTransferManager.DataRequested += DataTransferMangerDataRequested;
_dataTransferManager.TargetApplicationChosen += DataTransferMangerTargetApplicationChosen;
}
public override void OnNavigatedFrom(Dictionary<string, object> viewModelState, bool suspending)
{
base.OnNavigatedFrom(viewModelState, suspending);
// clean up events
_dataTransferManager.DataRequested -= DataTransferMangerDataRequested;
_dataTransferManager.TargetApplicationChosen -= DataTransferMangerTargetApplicationChosen;
}
private void DataTransferMangerTargetApplicationChosen(DataTransferManager sender, TargetApplicationChosenEventArgs args)
{
}
private void DataTransferMangerDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var request = args.Request;
var deferral = request.GetDeferral();
request.Data.Properties.Title = "title test";
request.Data.Properties.Description = "description test";
request.Data.SetText("test hello");
request.Data.SetUri(new Uri("https://www.google.com"));
request.FailWithDisplayText("fail");
deferral.Complete();
}
I have tried setting different properties in the DataRequested event but still nothing.
Does anyone know what it could be? Do I need to set some permissions?
Edit:
Ok, weird I tried this in a new solution with only this code and it is working fine. But no idea why its not working in my current solution.
Ok I found out what was causing the problem.
I had to remove this, since this will cancel the operation. (I thought this will show if it failed for some reason and not cancel directly).
request.FailWithDisplayText("fail");
I'm having trouble trying to navigate automatically between pages in my Windows 8.1 app based on a little check. It just doesn't want to navigate to another page when doing this in LoadState, as if something isn't loaded yet, but it doesn't give an error either. When I insert a delay using (for example) await Task.Delay(2000) before doing Frame.Navigate, then my app will redirect without any problem.
protected async override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
MyData oData = await getData();
if (oData != null)
{
this.Frame.Navigate(typeof(newPage), oData);
}
else
{
// do something else
}
}
Do I have to put this code in another load- or navigated-event? Or how can I make this work?
In LoadState and SaveState you should only save and restore the page state (called when suspending and reactivating the app). Do nothing else (like navigating).
Put your logic into the OnNavigatedTo method instead...
If you want to navigate from method that called when page is loads, you should place your navigation code to OnNavigatedTo(...). But do not forget to wrap your code in Dispatcher.RunAsync(...) - Frame navigation in xaml return false
I tried calling Frame.Navigate(...) from the OnNavigatedTo method but still the navigation didn't occur.
There are other answers which say use Dispatcher.RunAsync, but that feels like it's making assumptions about the threading model of Windows Phone.
Here's what I do: attach a handler to the Loaded event of the page instead, and put my "redirect" logic in there. Loaded fires after OnNavigateTo and after NavigationHelper_LoadState, but before the page has become visible.
public LaunchPadPage() {
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
this.Loaded += LaunchPadPage_Loaded;
this.app = (App)App.Current;
}
private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e) {
// Let's show the root zone items
// NB: In case we don't have this data yet, do nothing
if (app.Hierarchy != null)
{
DefaultViewModel["Items"] = app.Hierarchy.RootItems;
}
}
private void LaunchPadPage_Loaded(object sender, RoutedEventArgs e) {
// No data? Go to the downloads page instead.
if (app.Hierarchy == null)
{
Frame.Navigate(typeof(DownloadingPage));
}
}
I'm creating an app similar to the stock Messaging. But i'm very new to wp8 development and also to c# and .net
I'm using the Long List Selector to display messages. Messages are loaded on NavigatedTo event of the page. the handler is async as it is loading the data from a webservice, when there are 0 stored in local db, then it saves them in local database.
I would like to scroll to the last message after the data is loaded.
the page OnNavigated to
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
string contactId = "";
if (NavigationContext.QueryString.TryGetValue("contactId", out contactId))
{
await App.MessagesViewModel.LoadData();
DataContext = App.MessagesViewModel;
//scroll to last message, but it's apparently to soon
var lastMessage = App.MessagesViewModel.Messages.LastOrDefault();
llsMessages.ScrollTo(lastMessage);
}
}
but this throws an exception System.ArgumentException: The provided item doesn't exist in the collection. So i figured the list hasn't yet changed.
So i tried different events of LongListSelector that would indicate that it has already added the data from the view model. After a while of experimetnation i came up with this
private void llsMessages_SizeChanged(object sender, SizeChangedEventArgs e)
{
var lastMessage = App.MessagesViewModel.Messages.LastOrDefault();
if (lastMessage != null)
{
llsMessages.ScrollTo(lastMessage);
}
}
but this works only when the messages are loaded from the database. When loading from webservice the last message is null.
So after load i'm on the first message at top, then i navigate away from the page, then come back, the list scrolls to bottom. i would like to eliminate this, but i have no idea how.
is there any way how to accomplish this?
Maybe this will work:
private async Task DoAndScroll()
{
await App.MessagesViewModel.LoadData();
var lastMessage = App.MessagesViewModel.Messages.LastOrDefault();
llsMessages.ScrollTo(lastMessage);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string contactId = "";
if (NavigationContext.QueryString.TryGetValue("contactId", out contactId))
{
DataContext = App.MessagesViewModel;
DoAndScroll();
}
}
Try the ItemRealized event of the longlistselector. Check whether the currently processed item in the selector is the last item of your datacontext and if it is so, then scroll to that item. Below code worked for me.
private void longlist_ItemRealized(object sender, ItemRealizationEventArgs e)
{
Search.BindSearch search = e.Container.Content as Search.BindSearch;
if (search != null)
{
int offset = 0;
if (OCollectionBindSearch.Count - OCollectionBindSearch.IndexOf(search) <= offset)
{
longlist.ScrollTo(search);
}
}
}