setting Stack Layout Visibility to false at start - xaml

I am stuck with a problem.
I am having a stack layout which should be displayed according to a data loaded from api request
I have bound isVisible to the api Response it is working good but the problem is that the loading takes minute time the problem is once page opened thestack layout remains visible a bit time and then visibility is changed according to the response
here is my XAML
<StackLayout x:Name="FailureResult" IsVisible="{Binding PlaceDetails.isErrorScreen,Converter={StaticResource BoolConverter}}">
<Label Text="{StaticResource GooglePlaceNotFound}" />
</StackLayout>
please help me for the above situation. I want the stack layout visibility to false intially and then based on the response change the visbility
Thanks in advance

Use a separate Property for visibility of the StackLayout.
public bool StackLayoutIsVisible => PlaceDetails != null;
Then once the api call is done raise your on property changed method.
PlaceDetails = response;
OnPropertyChanged(nameof(StackLayoutIsVisible));

I have achieved it by binding with a property until the api request is completed. The bound property is initially set as false. Once the api request is completed the the bound property is changed according to the api result.
xaml
<StackLayout x:Name="FailureResult" IsVisible="{Binding ErrorVisibility }">
<Label Text="{StaticResource GooglePlaceNotFound}" />
</StackLayout>
ViewModel
private bool _errorVisibility = false;
public bool ErrorVisibility
{
get
{
return this._errorVisibility;
}
set
{
this._errorVisibility = value;
this.NotifyPropertyChanged();
}
}
APImethod()
{
ErrorVisibility = PlaceDetails.isErrorScreen;
}

Related

How to fire a command after clicking somewhere else?

So, I have a custom control, CusConA, that works basically like a textbox - you type amount of money that you need, and I have a button below, whom by getting clicked saves that amount(from CusConA) somewhere, and that is working fine.
But I want to try the same functionality basically by clicking anywhere on that page (something like OnBlur in asp.net), or to be precise, when my CusConA is not in focus anymore.
By doing what is shown with the --> in code, I achieved sort of a solution, this way when pressing anywhere, even if I never even tried to write an amount, the command is being executed.
So, to try to circle my question, I need this command to execute only after typing some amount, and clicking somewhere alse after. How can I do that?
<Frame
Margin="55,0"
Padding="0"
BorderColor="Blue"
CornerRadius="30">
<StackLayout Orientation="Horizontal">
<Label
Margin="10"
FontAttributes="Bold"
FontSize="20"
HorizontalTextAlignment="Center"
Text="RSD"
TextColor="Some text"
VerticalTextAlignment="Center" />
<customControls:CusConA
Margin="0,0,15,0"
HorizontalOptions="FillAndExpand"
Keyboard="Numeric"
Placeholder="0,00"
PlaceholderColor="Gray"
Text="Some text"
TextColor="Black" >
--> <customControls:CusConA.Behaviors>
<xct:EventToCommandBehavior EventName="Unfocused" Command="{Binding DoSomething}" ></xct:EventToCommandBehavior>
</customControls:CusConA.Behaviors>
</customControls:CusConA>
</StackLayout>
</Frame>
Can you change DoSomething to check whether the amount has been typed? Might involve adding a boolean property to your control:
bool CanExecute { get; set; }
Then have "amount" bound to a property whose setter sets CanExecute = true; or CanExecute = false;, depending on whether an amount has been typed. Something like:
string Amount
{
...
set {
_amount = value;
myControl.CanExecute = value.Count > 0;
}
}
Then change DoSomething body to
if (this.CanExecute) { ... }
Alternatively, other techniques can be used to have a change to Amount trigger a change to a property on myControl.
The essential points are:
Adding CanExecute property, so control can be told when it is valid to execute that command.
Using some technique to bind or trigger myControl.CanExecute change, from elsewhere.
I think you can use EventToCommandBehavior to achieve this function.
There is an example of an EventToCommandBehavior in the Xamarin.Forms samples (see here).
<ContentPage.BindingContext>
<focusapp:MyViewModel></focusapp:MyViewModel>
</ContentPage.BindingContext>
<StackLayout>
<Entry>
<Entry.Behaviors>
<Behaviors:EventToCommandBehavior
EventName="Unfocused"
Command="{Binding EntryUnfocused}" />
</Entry.Behaviors>
</Entry>
</StackLayout>
And define EntryUnfocused in your viewmodel.cs (e.g. MyViewModel) just as follows:
MyViewModel.cs
public class MyViewModel
{
public ICommand EntryUnfocused { get; protected set; }
public MyViewModel() {
EntryUnfocused = new Command(CompletedCommandExecutedAsync);
}
private void CompletedCommandExecutedAsync(object param)
{
System.Diagnostics.Debug.WriteLine("------------> come here....");
}
}

ContentView - Binding Context is set to null

Currently I am playing around with .Net Maui but I maybe it's the same behavior as Xamarin.
I've created a simple Search-Control which is based on a ContentView.
ObjectSearchControl.xaml
<ContentView
x:Class="DeepBlue.Controls.ObjectSearchControl"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:DeepBlue.Converter"
xmlns:selector="clr-namespace:DeepBlue.Helpers"
x:Name="MySearchControl">
<StackLayout
Orientation="Vertical"
VerticalOptions="FillAndExpand">
<SearchBar
x:Name="ObjectSearchBar"
IsSpellCheckEnabled="False"
Keyboard="Text"
Placeholder="{Binding SearchBarPlaceholderText}"
TextChanged="ObjectSearchBar_TextChanged" />
<CollectionView
x:Name="ObjectResultView"
HeightRequest="500"
ItemsSource="{Binding DataSource}"
SelectionChanged="ObjectResultView_SelectionChanged">
</CollectionView>
</StackLayout>
</ContentView>
ObjectSearchControl.xaml.cs
public partial class ObjectSearchControl : ContentView
{
public static readonly BindableProperty SearchBarPlaceholderTextProperty
= BindableProperty.Create(nameof(SearchBarPlaceholderText), typeof(string),
typeof(ObjectSearchControl), string.Empty);
public static readonly BindableProperty DataSourceProperty
= BindableProperty.Create(nameof(DataSource), typeof(object),
typeof(ObjectSearchControl));
public ObjectSearchControl()
{
InitializeComponent();
Content.BindingContext = this;
}
public string SearchBarPlaceholderText
{
get => (string)GetValue(SearchBarPlaceholderTextProperty);
set => SetValue(SearchBarPlaceholderTextProperty, value);
}
public object DataSource
{
get => (object)GetValue(DataSourceProperty);
set => SetValue(DataSourceProperty, value);
}
}
This ContentView I've inserted in my Page
<StackLayout
x:Name="SelectFishingSection"
HeightRequest="600"
HorizontalOptions="FillAndExpand"
Orientation="Vertical"
VerticalOptions="FillAndExpand">
<controls:ObjectSearchControl
DataSource="{Binding NonFilterdDataSource}"
HeightRequest="550"
HorizontalOptions="FillAndExpand"
SearchBarPlaceholderText="Placeholder"
VerticalOptions="FillAndExpand" />
</StackLayout>
After running the code the control rendered and the Placeholdertext in the SearchBar is set correct. So I thought the implementation of the binding is correct. But in my CollectionView no element is rendered.
So I debugged a lot and found out that the BindingContext is set 2 times. When I initialize the control all the properties have got NULL values. -> seems okay
Then the control is appearing and I get the elements from DB and set them to "DataSource".
<ContentPage.Behaviors>
<mctBehaviors:EventToCommandBehavior Command="{Binding SetDataSourcesCommand}" EventName="Appearing" />
</ContentPage.Behaviors>
private async Task SetDataSources()
{
try
{
IsBusy = true;
NonFilterdDataSource = new ObservableCollection<MyTestModel>(await DataService.GetAll());
}
finally
{
IsBusy = false;
}
}
That's also called and seems correct. After that BindingContext is set (OnBindingContextChanged is called in my ObjectSearchControl.xaml.cs) and all properties (SearchBarPlaceholderText and DataSource) have got correct values. At this point in DataSource there are 9 Elements!
If I continue debugging the DataSource is set to NULL and also the BindingContext is set to NULL! But I don't understand why?
Output window in VS shows only "External Code" and I can not figure out why this is happening.
I found a few similar questions but none of the could solve my problem.
After analysing the "External Code" in VS I found out that the source of the problem must be somewhere in the measurement for the control. So I removed VerticalOptions="FillAndExpand" from my controls:ObjectSearchControl implemention and after that the problem was gone. BindingContext is set only one time and everything is working as expected!

Localize strings in XAML UI in UWP

I have a resource entry named info_278 in the Resources.resw file on my UWP app. I have 3 scenarios where I need to use this resource but looks like I need to duplicate this to cater to different scenarios. Scenarios are as follows.
Error message content from code
var displayErrorOnPopup = ResourceHandler.Get("info_278");
TextBlock Text property from XAML (Looks like a new entry needed as info_278.Text)
<TextBlock x:Uid="info_278" Margin="10,0,0,0" />
Button Content property from XAML (Looks like a new entry needed as info_278.Content)
<Button x:Uid="info_278" Margin="10,0,0,0" />
How do I proceed without duplicating this resource in the .resw file?
The only way to avoid duplication is to set the string value in code-behind using ResourceLoader. Because you could direct access to the specific property of the target control. Like this:
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
this.TextBlock.Text = resourceLoader.GetString("info_278");
If you are not going to do it in the code behind, then I have to say there is no way to avoid the duplication of the resource string. You should add info_278.Text and info_278.Content for different XAML scenarios.
You could create a markup extension. I've used this in WinUI 3, but should work in UWP too.
using Microsoft.UI.Xaml.Markup;
using Windows.ApplicationModel.Resources;
namespace MyApp;
[MarkupExtensionReturnType(ReturnType = typeof(string))]
public class StringResourceExtension : MarkupExtension
{
private static readonly ResourceLoader _resourceLoader = new();
public StringResourceExtension() { }
public string Key { get; set; } = "";
protected override object ProvideValue()
{
return _resourceLoader.GetString(Key);
}
}
Then in the XAML:
...
local="using:MyApp"
...
<TextBlock Text="{local:StringResource Key=info_278}" />
<Button Content="{local:StringResource Key=info_278}" />
The Content of Button can be a TextBlock:
<Button>
<TextBlock x:Uid="MyTextId" Style="{StaticResource MyTextBlockStyle}" />
</Button>

Xamarin xaml binding

I am trying to bind my xaml to a property in my view model, but it doesn't work as I expected.
The following code works, but it seems to create a new instance of the mainwindowviewmodel object which will result in problem.
<Label Text="{Binding Path=Test}" >
<Label.BindingContext>
<local:MainWindowViewModel />
</Label.BindingContext>
</Label>
The following doesn't work at all.
<Label Text="{Binding Path=Test}" >
</Label>
I've the property Test in my view model.
What am I doing wrong?
Make sure to set the BindingContext of the View to an instance of the model (MainWindowViewModel) to make the second code snippet work.
For example in the constructor of the view's code behind
public MainWindow() {
InitializeComponents();
var viewModel = new MainWindowViewModel();
this.BindingContext = viewModel;
}
Or directly in the View
<MainWindow.BindingContext>
<local:MainWindowViewModel />
</MainWindow.BindingContext>
The both above are technically equivalent.

How to mix dynamic and static items in UWP XAML NavigationView MenuItems?

I'm trying to make a NavigationViewMenu and I need a menu layed out as follows
static Home item
static Header
dynamic elements from DB as items
static Header
static set of items
This is what I tried:
<NavigationView.MenuItems>
<NavigationViewItem Icon="Home" Content="Home" Tag="home" />
<NavigationViewItemSeparator />
<NavigationViewItemHeader Content="My Stuff"/>
<NavigationViewList ItemsSource="{x:Bind MyStuff}">
<NavigationViewList.ItemTemplate>
<DataTemplate x:DataType="local:MyModel">
<NavigationViewItem Icon="Pictures" Content="{x:Bind Name}" Tag="{x:Bind Tag}" />
</DataTemplate>
</NavigationViewList.ItemTemplate>
</NavigationViewList>
<!-- Static equivalent to the above:
<NavigationViewItem Icon="Pictures" Content="Woop" Tag="foos"/>
<NavigationViewItem Icon="Pictures" Content="Doop" Tag="foos"/>
<NavigationViewItem Icon="Pictures" Content="Loop" Tag="foos"/>
-->
<NavigationViewItemHeader Content="Other Stuff"/>
<NavigationViewItem Icon="Pictures" Content="Foos" Tag="foos"/>
<NavigationViewItem Icon="ContactInfo" Content="Bars" Tag="bars"/>
<NavigationViewItem Icon="SwitchApps" Content="Bazes" Tag="bazes"/>
</NavigationView.MenuItems>
This is what I've got:
This is what I wanted:
Is there anything as good and practical as Angular's *ngFor in XAML for UWP?
I ran into the same behavior, and managed to find a work around. In my case, I had two lists of menu items (dynamically data-bound items), and I wanted to use NavigationViewItemHeader on top of both (static items). I tried using a NavigationViewList and ran into your problem.
TL;DR:
Create a list of menu items in C# code. The elements of this list can be a mix of your viewmodels, and any static Navigation Items (headers, separators, etc). Then use a DataTemplateSelector to either databind to your viewmodel or pass-through the navigation items unchanged.
More detailed
In your C# code-behind, create an enumerable (or observable collection) of your menu items. In my case SomeCollection and AnotherCollection represent my data sources that I wanted to bind to my NavigationView. I have to type it as object because it's a mix of my viewmodels and the built-in UWP navigation item types.
private IEnumerable<object> MenuItems()
{
yield return new NavigationViewItemHeader { Content = "Some List" };
foreach (var some in SomeCollection)
{
yield return some;
}
yield return new NavigationViewItemHeader { Content = "Another List" };
foreach (var another in AnotherCollection)
{
yield return another;
}
}
// somewhere else, like in your Page constructor or a CollectionChanged handler
this.NavigationList = MenuItems().ToList();
Second, create a Data Template Selector to switch between your template and the navigation items:
class NavigationItemTemplateSelector : DataTemplateSelector
{
public DataTemplate ViewModelTemplate{ get; set; }
public DataTemplate NavigationItemTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
return item is MyViewModel
? ViewModelTemplate
: NavigationItemTemplate;
}
}
Finally, change your NavigationView to reference the template selector and menu item source. The NavigationItemTemplate is just a pass-through, and your ViewModelTemplate would have the normal viewmodel item binding logic.
<Page.Resources>
<DataTemplate x:Key="ViewModelTemplate" x:DataType="local:MyViewModel">
<TextBlock Text="{x:Bind SomeProperty}" />
</DataTemplate>
<DataTemplate x:Key="NavigationItemTemplate">
</DataTemplate>
<local:NavigationItemTemplateSelector x:Key="NavigationItemTemplateSelector"
ViewModelTemplate="{StaticResource ViewModelTemplate}"
NavigationItemTemplate="{StaticResource NavigationItemTemplate}" />
</Page.Resources>
<NavigationView
MenuItemsSource="{x:Bind NavigationList, Mode=OneWay}"
MenuItemTemplateSelector="{StaticResource NavigationItemTemplateSelector}">
<Frame x:Name="ContentFrame"></Frame>
</NavigationView>
I can reproduce it. It looks like NavigationViewList only take the space of one item when putting itself in NavigationView.MenuItem. Which is the same like putting a ListView in a ListViewItem. To change this behavior we need to change the item's behaviour ourselves. However after some investigating it seems currently customization of NavigationViewList is blackbox for us. So the only way I could think is to build our own NavigationView with the help of splitview and acrylic.
I didn't find it necessary to use different templates as in the accepted answer, maybe because there were some changes in the underlying Windows code in the meantime. As I needed a stable part of the menu and then a dynamic part depending on the actual page, I created an interface:
interface IMenuProvider {
IEnumerable<NavigationViewItemBase> GetMenuItems();
}
and made sure all my pages implement it. My MainPage returns the fixed part:
public IEnumerable<NavigationViewItemBase> GetMenuItems() {
yield return new NavigationViewItem {
Tag = "home",
Icon = new SymbolIcon(Symbol.Home),
Content = "Home",
};
yield return new NavigationViewItemSeparator();
yield return new NavigationViewItem {
Tag = "xxx",
Icon = new SymbolIcon(Symbol.XXX),
Content = "XXX",
};
}
the other pages, similary, provide their own menu headers and items.
When I navigate the pages, I change the menu as well, concatenating the fixed and variable parts:
ContentFrame.Navigate(PageType, null, transitionInfo);
if (ContentFrame.Content is IMenuProvider menuProvider)
= GetMenuItems().Concat(menuProvider.GetMenuItems()).ToList();
(Or, you might place the menu change into the Navigated handler of the Frame.)
While it's still a nuisance that these menus, at least the fixed part, cannot be declared in XAML, this approach works.