Pulling my hair out at the point. My Picker is showing an annoying line on the UI and/or the Picker's Title property if that's enabled. I simply want the Picker, not the stuff showing on the UI beneath it. Any idea on how to achieve this? Do I have to use a custom renderer or is there something simple I'm missing?
Note: The list is intentionally empty in the below examples.
Without the title, I click the Existing button, the line shows, click it again and the modal appears:
With the title, I click the Existing button, the line and title show, click it again and the modal appears:
Don't know why I have to click the button twice. But it's only on the initial page load. If I exit the modal and click the button again, it immediately appears, no double-click. Not sure if that's related to my original question, but thought I'd include it for additional information.
NewSubjectPage.xaml (chopped for brevity)
<ContentPage.Content>
<StackLayout x:Name="NewSubjectMainLay">
<ScrollView>
<StackLayout x:Name="NewSubjectChildLay">
<Grid>
<Button
x:Name="NewSubjectExisChrtBtn"
Clicked="NewSubjectExisChrtBtn_Clicked"
Grid.Column="2"
Text="Existing" />
</Grid>
</StackLayout>
</ScrollView>
<Picker
x:Name="NewSubjectExisChrtPck"
IsVisible="False"
ItemsSource="{Binding Charts}"
ItemDisplayBinding="{Binding Name}"
SelectedIndexChanged="NewSubjectExisChrtPck_SelectedIndexChanged"
Title="Select chart"
Unfocused="NewSubjectExisChrtPck_Unfocused"/>
</StackLayout>
</ContentPage.Content>
NewSubjectPage.xaml.cs (chopped for brevity)
public partial class NewSubjectPage : ContentPage
{
private string chartName;
private readonly NewSubjectViewModel _viewModel;
public string ChartName
{
get => chartName;
private set
{
chartName = value;
OnPropertyChanged();
}
}
public NewSubjectPage()
{
InitializeComponent();
BindingContext = _viewModel = new NewSubjectViewModel();
chartName = "";
}
private void NewSubjectExisChrtBtn_Clicked(object sender, EventArgs e)
{
_viewModel.LoadChartsCommand.Execute(null);
NewSubjectExisChrtPck.IsVisible = true;
NewSubjectExisChrtPck.Focus();
}
private void NewSubjectExisChrtPck_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = (Picker)sender;
int selectedIndex = picker.SelectedIndex;
if (selectedIndex != -1)
{
ChartName = picker.Items[picker.SelectedIndex];
}
}
private void NewSubjectExisChrtPck_Unfocused(object sender, FocusEventArgs e)
{
NewSubjectExisChrtPck.IsVisible = false;
NewSubjectExisChrtPck.Unfocus();
}
}
NewSubjectViewModel.cs (chopped for brevity)
class NewSubjectViewModel : BaseViewModel
{
private ObservableCollection<Chart> charts;
public ObservableCollection<Chart> Charts
{
get { return charts; }
private set
{
charts = value;
OnPropertyChanged();
}
}
public Command LoadChartsCommand { get; set; }
public NewSubjectViewModel()
{
LoadChartsCommand =
new Command(
async () => await ExecuteLoadChartsCommand()
);
}
private async Task ExecuteLoadChartsCommand()
{
try
{
IndicatorRunning = true;
var list = await App.Database.GetChartsAsync();
Charts = new ObservableCollection<Chart>(list);
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
}
Thanks for your help! Let me know if you need to see anything else.
First, I was not able to reproduce the issue of the modal not showing until a second click of the button. You might need to provide more code for that to happen. To even use your code sample I had to replace var list = await App.Database.GetChartsAsync(); with something else to simulate a long running task that returns an empty list. Also had to create a Chart type with a Name property. Not to mention BaseViewModel. In the future, please provide all code to reproduce the issue so there is minimal work required of the person who is trying to help you. There is concept on Stack Overflow called the MCVE (minimal, complete, verifiable example): http://stackoverflow.com/help/mcve
That said, perhaps the first click is actually focusing the emulator and making it the active app, and then the second is the first actual click on the button? This I can reproduce. IOW, if the emulator is not the foreground app, you have to click it once to make it active and then your app will handle clicks.
As for the undesirable UI, you do realize that the Picker UI is basically a clickable label that when clicked displays the actual picker modal? So when you make it visible, what you are making visible is the label UI, which has the line and the Title (if set), and when you focus that label, then the actual picker dialog is displayed. If you don't want to see the UI Label at all, then why make it visible? You can focus it without making it visible, so just remove the line NewSubjectExisChrtPck.IsVisible = true;
As a side note, when you call _viewModel.LoadChartsCommand.Execute(null); that calls an async method, var list = await App.Database.GetChartsAsync(); , so the LoadChartsCommand returns before you set the Charts property, and also then the code following the call to _viewModel.LoadChartsCommand.Execute(null); also executes before LoadChartsCommand really finishes, so you are making the picker visible and focusing it before the LoadChartsCommand finishes as well, so if you were loading actual items for the picker to display, they may not be there the first time. Maybe it's just the sample code, but I see no reason to use a command here, but rather you should just call an awaitable task. You are not binding to the LoadChartsCommand, so I see no reason for you to even use a Command in this scenario. Instead I suggest making ExecuteLoadChartsCommand public and calling it directly, e.g.:
private async void NewSubjectExisChrtBtn_Clicked(object sender, EventArgs e)
{
//_viewModel.LoadChartsCommand.Execute(null); // Returns immediately, so picker not loaded with items yet.
await _viewModel.ExecuteLoadChartsCommand(); // Waits for method to finish before before presenting the picker.
//NewSubjectExisChrtPck.IsVisible = true;
NewSubjectExisChrtPck.Focus();
}
Related
I am not quite sure if I am asking the right question. I assume other people have had this issue.
I built my own Blazor Grid component. I am using an bound to a property.
I have a function to load my grid. I changed my bound property to a full getter,setter. In the setter, I call my function to load the grid. This works fast and easy in pretty much all instances. But, I have one grid that when binding it will take a few extra seconds to complete.
The problem: I can't seem to figure out how to get my waiting spinner component to show when loading my grid.
Example Blazor Markup:
#if (dataGrid == null)
{
<hr />
<BitcoSpinner></BitcoSpinner>
}
else
{
<BitcoGrid TheGrid="dataGrid"></BitcoGrid>
}
Here is my property and GridLoading:
private string selectedGroup1 = "";
public string selectedGroup
{
get => selectedGroup1;
set
{
selectedGroup1 = value;
LoadGrid();
}
}
private void LoadGrid()
{
dataGrid = null;
PT_Grid_Admin ptGrid = new PT_Grid_Admin(permitTraxLibrary, gridParams);
dataGrid = ptGrid.ADMIN_FeeList(feeList.Fee_Key, selectedGroup);
}
You should define LoadGrid method asynchronously. Therefore, at the beginning of the program, when the data grid value is set, your spinner will be displayed until the data grid value is not received. Then, after receiving the data grid value, the else part of the condition will be executed and its value will be displayed to the user.
It may not take much time to receive information from the DB in local mode, so the following code can be used to simulate the delay:
System.Threading.Thread.Sleep(5000);
In general, I think that if your code changes like this, you can see the spinner.
private string selectedGroup1 = "";
public string selectedGroup
{
get => selectedGroup1;
set
{
selectedGroup1 = value;
LoadGrid();
}
}
private async Task LoadGrid()
{
dataGrid = null;
System.Threading.Thread.Sleep(5000);
.
.
}
Of course, it is better to load the datagrid in OnInitializedAsync method. For more info you can refer to this link.
My code in MainPage.cs
ApplicationLanguages.PrimaryLanguageOverride = "ja-jp";
XAML
<Button content="Click" Click="Button_Click" />
after this i've opened dialog with my code
private async void Button_Click(object sender, RoutedEventArgs e)
{
{
testDialog dialog = new testDialog();
await dialog.ShowAsync();
}
}
My testDialog's XAML code
<TextBlock x:Uid="TestTextBlock" />
i've define the language's text in Resources.resw file, it worked fine if i put textblock in current MainPage but when i put it in dialog the textblock's text doesn't change, It only change after i reset the application. Any ideas how can i fix this guys ?'
Setting new language:
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "ja-jp";
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Reset();
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForViewIndependentUse().Reset();
Reload Current page:
private bool Reload(object param = null)
{
var type = Frame.CurrentSourcePageType;
try
{
return Frame.Navigate(type, param);
}
finally
{
Frame.BackStack.Remove(Frame.BackStack.Last());
}
}
Also you can use Frame.Navigate(this.GetType()); for refreshing current page UI.
please take a look at this post for more information: Dynamically change the language of a universal app
I would like to print the movie information from the tmdb api and print the movie information into the usercontrols. I want the program to show a poster, its name, its release date, how many votes and so on. My problem starts right here, I want to load usercontrolls as async, so I want all of them loaded in my main window at the same time. But I can't do it, they're loading one by one. Is there a way to use Async in the UI update? Or is there another way to achieve this? I want to get the names of all 20 movie posters at the same time as the webpages and get these 20 usercontrol added to my main window at the same time. I am using this code right now:
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
await GetPopularMoviesAsync();
}
public async Task GetPopularMoviesAsync()
{
SearchContainer<SearchMovie> popularMovies = await client.GetMoviePopularListAsync("en", 1);
List<SearchMovie> popularMovieList = popularMovies.Results;
foreach (var searchMovie in popularMovieList)
{
MovieUSC mov = new MovieUSC();
var image = await GetMovieImage(searchMovie);
GetPosterFromFile(image, mov.MoviePoster);
mov.Name = "PopularMovies" + searchMovie.Id;
mov.MovieName.Text = searchMovie.OriginalTitle;
mov.MovieReleaseDate.Text = "(" + searchMovie.ReleaseDate.Value.Year + ")";
mov.MovieRatingBar.Value = Convert.ToInt32(searchMovie.VoteAverage) / 2;
mov.ClickedMovie += ClickedMovie;
MoviePanel.Items.Add(mov);
}
}
Even if you don't want to go the whole MVVM route, it's still good practice to separate data access from the UI.
Use an ObservableCollection<> to hold the search results, which will be the driver of the display - this will automatically update any bound controls each time it is refreshed.
public ObservableCollection<SearchMovie> PopularMovies { get; }
= new public ObservableCollection<SearchMovie>();
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
var popularMovies = await client.GetMoviePopularListAsync("en", 1);
PopularMovies.Clear();
foreach(var movie in popularMovies)
PopularMovies.Add(movie);
}
Your window should use either a ListBox if you want the user to be able to select a specific movie item, otherwise an ItemsControl. In either case, set ItemsSource to the PopularMovies collection. Use a DataTemplate to define the layout for each movie item - each control is bound to the appropriate property of the SearchMovie object.
<ItemsContol x:Name="MoviesDisplay">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding OriginalTitle}" />
<TextBlock Text="{Binding ReleaseDate.Value.Year}" />
// Add more controls here as required
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I'm using an AutoSuggestBox with grouping support using the style found here, but I'm having a weird issue in how the suggestion popup is shown on mobile (on desktop is 100% fine, I can see the grouped list inside the popup).
What happens is that, while I'm writing in the AutoSuggestBox, an ungrouped popup with wrong positioning is shown, as you can see here:
This lasts until I select one of the items, but the selection provides even weirder results by showing the correct popup, even if it's filtered with the text that I had before selecting the item.
You can see the correct behavior on desktop here:
<AutoSuggestBox Style="{StaticResource GroupedAutoSuggestBoxStyle}"
UpdateTextOnSelect="True"
TextMemberPath="Name"
VerticalAlignment="Center"
ItemsSource="{x:Bind ServicesCollectionViewSource.View, Mode=OneWay}"
TextChanged="ServicesFilterAutoSuggestBox_OnTextChanged"
QuerySubmitted="ServicesFilterAutoSuggestBox_OnQuerySubmitted"
LostFocus="ServicesFilterAutoSuggestBox_OnLostFocus"
AutoMaximizeSuggestionArea="True"
Grid.Column="1">
...
private void ServicesFilterAutoSuggestBox_OnTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason != AutoSuggestionBoxTextChangeReason.UserInput) return;
if (string.IsNullOrEmpty(sender.Text) && ViewModel.AccountService != null)
{
// User canceled previous selection, so we reset it
ViewModel.AccountService = null;
}
// Filter the results
ServicesCollectionViewSource.Source = from services
in ViewModel.Services
where services.Name.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant())
group services by services.Section.Id;
}
private void ServicesFilterAutoSuggestBox_OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion != null)
{
ViewModel.AccountService = (Service) args.ChosenSuggestion;
}
else
{
ViewModel.AccountService = null;
}
}
I don't understand why this works fine on desktop while it has this weird behavior on mobile, and I really need your help.
So having a play with PRISM and I have a grid who's Visibility property is bound to a property in a view model like so.
Xaml
Grid Grid.RowSpan="2" Grid.ColumnSpan="3" Background="#7F808080" Visibility="{Binding LoadingVisibility}">
Code Behind
private Visibility loadingVisibility = Visibility.Visible;
public Visibility LoadingVisibility
{
get
{
return loadingVisibility;
}
set
{
SetProperty(ref loadingVisibility, value);
}
}
Now if I do this LoadingVisibility = Visibility.Collapsed;, the grid does not disappear and is still visible.
If I then set a breakpoint at SetProperty(ref loadingVisibility, value); I can see the original value of loadingVisibility, which is set as Visibile, and I can see that value is set to Collapsed.
If I then step on I can see loadingVisiblity has now changed to collapsed as it should. At this point I expect the Grid to be notified which in turn executes 'Get' to retrieve the value. This does not occur.
The binding is working because the Get is called when loading up and if I change private Visibility loadingVisibility = Visibility.Visible; to Collapsed and run the code the grid starts invisible.
So my question is, after SetProperty is executed, why is the Get not?
EDIT:
Just so you can see where I set the property.
public ShellViewModel(IEventAggregator IEventAggregator)
{
IEventAggregator.GetEvent<PubSubEvent<HardwareLoaded>>().Subscribe(x =>
{
if (!x.HardwareOK)
{
MessageBox.Show("There was an issue loading hardware. See Log");
}
LoadingVisibility = Visibility.Collapsed;
});
}
EDIT 2:
Just found something interesting, if I comment out LoadingVisibility = Visibility.Collapsed; in the Subscribe and then add a button to the xaml and have the click event like so then everything works fine.
private void Button_Click(object sender, RoutedEventArgs e)
{
mvm.LoadingVisibility = Visibility.Collapsed;
}
So now I guess the question is why, when both methods call the set property, does only one fully work and cause the Get to work?
Quite often when using the EventAggregator, you're working on the UI thread. But seeing the HardwareLoaded type it came to me that you might be doing some checking on another thread. And as we know, bindings have to be updated from the UI thread. Normally you would use Dispather.BeginInvoke, but Prism's EventAggregator has an overload in the Subscribe method to tell the handler to offload to the UI thread.
IEventAggregator.GetEvent<PubSubEvent<HardwareLoaded>>().Subscribe(x =>
{
if (!x.HardwareOK)
{
MessageBox.Show("There was an issue loading hardware. See Log");
}
LoadingVisibility = Visibility.Collapsed;
}, ThreadOption.UIThread);