I am trying to dynamically show/hide button inside Xamarin Forms ContentPage.
I have two buttons in my XAML code:
<StackLayout Orientation="Vertical">
<Button x:Name="start_btn" Clicked="startPanic">
<Button.Text>START</Button.Text>
</Button>
<Button x:Name="stop_btn" IsVisible="false">
<Button.Text>STOP</Button.Text>
</Button>
</StackLayout>
Corresponding C# code:
public partial class PanicPage : ContentPage
{
private Button startBtn;
private Button stopBtn;
public PanicPage ()
{
InitializeComponent ();
startBtn = this.FindByName<Button> ("start_btn");
stopBtn = this.FindByName<Button> ("stop_btn");
}
private void startPanic(object sender, EventArgs args){
Device.BeginInvokeOnMainThread (() => {
startBtn.IsVisible = false;
stopBtn.IsVisible = true; // DOESN'T WORK, button still will be hidden
});
}
}
When I set isVisible property in XAML, it doesn't react for any property change in event method (startPanic). How can I fix it?
Change your code in xmal file and write properties for start and stop button
<Button x:Name="start_btn" Clicked="startPanic" IsVisible="{Binding IsStartVisible}">
<Button.Text>START</Button.Text>
</Button>
<Button x:Name="stop_btn" IsVisible="{Binding IsStopVisible}">
<Button.Text>STOP</Button.Text>
</Button>
In ViewModel write following property and similar for start button and set IsStopVisible =true/false based on your logic
private bool _isStopVisible;
public bool IsStopVisible{
get {
return _isStopVisible;
}
set {
_isStopVisible= value;
RaisePropertyChanged ("IsStopVisible");
}
}
Maybe I'm late but I was searching this too without success. This may be useful for someone.
objectView.SetValue(IsVisibleProperty, false); // the view is GONE, not invisible
objectView.SetValue(IsVisibleProperty, true);
It should work just fine. I copied your code and cleaned it up a bit, it shows the STOP button, then I
A few remarks:
use the short property where possible <Button Text="X"/>, it's
easier to read
when you add a XAML page the IDE adds a .xaml.cs file next to it and generates another .g.cs that you don't see. The .g.cs file
contains generated code that finds all the x:Name'd elements and
defines placeholders for them, no need to find them by name yourself
all UI-initiated events are executed on the UI thread, no need to do that explicitly
Here's the XAML, same as yours just tighter and added Margin so the button is visible
<StackLayout Orientation="Vertical" Margin="20">
<Button x:Name="start_btn" Clicked="startPanic" Text="START" />
<Button x:Name="stop_btn" Text="STOP" IsVisible="false" />
</StackLayout>
And the code behind:
public partial class TestPage : ContentPage
{
public TestPage ()
{
InitializeComponent ();
}
private void startPanic(object sender, EventArgs args){
Device.BeginInvokeOnMainThread (() => {
start_btn.IsVisible = false;
stop_btn.IsVisible = true;
});
}
}
Use the Visibility property of view.
for example if u want to make your button invisible you can do
if(condition)
{
button.Visibility=ViewStates.Invisible;
}
else
{
button.Visibility=ViewStates.Visible;
}
Related
I have xamarin page that shows list of products and a picker which represents type of product.
My problem is that when i start application and try to access page for the first time,
in debugging mode i can see that list i am using as ItemsSource has value,
But when page is loaded picker is greyed out and doesnt have any data.
When I leave page with picker and open it up second time, picker is loaded with data!
Here is my code!
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Converters="clr-namespace:eProdaja.Mobile.Converters"
mc:Ignorable="d"
x:Class="Restoran.Mobile.Views.ProizvodiPage">
<ContentPage.Resources>
<ResourceDictionary>
<Converters:ImageConverter x:Key="imgConv"></Converters:ImageConverter>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<Picker ItemsSource="{Binding TipProizvodaList}" ItemDisplayBinding="{Binding Naziv}" SelectedItem="{Binding SelectedTipProizvoda}"></Picker>
<ListView ItemsSource="{Binding ProizvodiList}" ItemSelected="ListView_ItemSelected" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10" Margin="5" HorizontalOptions="CenterAndExpand" >
<Image Source="{Binding Slika, Converter={StaticResource imgConv}}" ></Image>
<Label Text="{Binding Naziv}"
d:Text="{Binding .}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
<Button HorizontalOptions="Center" BorderColor="Transparent" BackgroundColor="Transparent" TextColor="OrangeRed" Text="Dodaj u košaricu"></Button>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
Code inside of xaml.cs
private ProizvodiViewModel model = null;
public ProizvodiPage()
{
InitializeComponent();
BindingContext = model = new ProizvodiViewModel();
}
protected async override void OnAppearing()
{
base.OnAppearing();
await model.Init();
}
Code inside of viewModel
public ObservableCollection<TipProizvoda> TipProizvodaList { get; set; } = new ObservableCollection<TipProizvoda>();
public async Task Init()
{
if (TipProizvodaList.Count == 0)
{
var TPList = await _tipProizvoda.Get<List<TipProizvoda>>(null);
TipProizvodaList.Clear();
TipProizvoda empty = new TipProizvoda { TipProizvodaID = 0, Naziv = "" };
TipProizvodaList.Add(empty);
foreach (var tipProizvoda in TPList)
{
TipProizvodaList.Add(tipProizvoda);
}
}
}
Over the years, I've had various problems attempting to do ANYTHING in OnAppearing that affects what is seen on screen.
There must be something Xamarin does AFTER OnAppearing, that gets the page into a valid state to receive binding changes.
One way around this limitation is to delay the work that you want done, so that OnAppearing returns before your work sets the bindings.
The "downside" of this, is that the page will appear (at first) without your work. See the two references to "activityIndicator" - those are where you control what you want user to see before your work is ready.
The "upside" of this, is that it ensures Xamarin "sees" your binding changes. (It also provides a place to do slow background work, if that is needed.)
Try this:
public partial class MyPage : ...
{
protected override void OnAppearing()
{
base.OnAppearing();
This returns immediately, allowing OnAppearing to return.
DelayWork(100, BackgroundWork, UIWork);
}
// Custom class, used to pass results from background work to UI work.
class BackgroundResult {
...
}
private void DelayWork(int milliseconds, Func<BackgroundResult> backgroundWork, Action uiWork)
{
//OPTIONAL activityIndicator.IsRunning = true;
Task.Run( () => {
// The delay ensures Xamarin page preparation has a little time, before your work begins.
// Without this delay, under some circumstances, the page might not show up as quickly.
// You might not need this.
Task.Delay(milliseconds);
// Slow work -- do nothing that affects UI.
BackgroundResult backgroundResult = BackgroundWork();
Device.BeginInvokeOnMainThread(async () => {
await uiWork(backgroundResult);
});
});
}
private BackgroundResult BackgroundWork()
{
// Slow work -- do nothing that affects UI.
...
// fill this with whatever info you need to pass to UIWork.
var backgroundResult = new BackgroundResult();
// ...
return backgroundResult;
}
private async void UIWork(BackgroundResult backgroundResult)
{
// Work that affects UI, possibly via Bindings.
await model.Init();
//OPTIONAL activityIndicator.IsRunning = false;
}
}
In your situation, you might not need BackgroundWork nor BackgroundResult. Shown for completeness.
I want to make a comment entry that is only visible if the user is at the bottom of the article.
So the app has to recognize when the user has scrolled enough, then a method should make the entryfield visible.
I can't find something like this on the Internet, so maybe you guys can help me.
This one is without the entryfield and when the user scrolls down ...
... the entryfield becomes visible
If you are using a ScollView, there is a Scrolled event that fires whenever the view is scrolled and the ScrolledEventArgs contain ScrollX and ScrollY properties that allow you to know where the ScrollView currently is. If you compare ScrollY to the height of the ContentSize property of the ScrollView, e.g.:
XAML:
<StackLayout>
<ScrollView x:Name="scrollView" Scrolled="Handle_Scrolled">
<StackLayout>
<Label Text="{Binding Article}" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand" />
</StackLayout>
</ScrollView>
<Entry IsVisible="{Binding AtEnd}" Placeholder="End reached!" />
</StackLayout>
Code behind (MainPage is a ContentPage subclass):
string _article;
public string Article
{
get
{
return _article;
}
set
{
if (_article != value)
{
_article = value;
OnPropertyChanged("Article");
}
}
}
bool atEnd;
public bool AtEnd
{
get
{
return atEnd;
}
set
{
if (atEnd != value)
{
atEnd = value;
OnPropertyChanged("AtEnd");
}
}
}
public MainPage()
{
Article = "<put in enough text here to force scrolling>";
AtEnd = false;
InitializeComponent();
BindingContext = this;
}
void Handle_Scrolled(object sender, Xamarin.Forms.ScrolledEventArgs e)
{
if (e.ScrollY + scrollView.Height >= scrollView.ContentSize.Height)
AtEnd = true;
else
AtEnd = false;
}
That said, why not just put the entry below the article using the same scroll view? IOW just put the Entry element after the Label above in the same StackLayout and the Entry will just be there at the end always, but the user won't see it until they scroll down. Seems that that would be a simpler solution. Of course you may not be using a Label but the same applies, just put the Entry at the bottom of the layout that the ScrollView is scrolling.
How to Lable Hide and show if admin is login then one label is displayed otherwise label is hidden {How Solve it} ?
xaml Code
<StackLayout IsVisible="{Binding IsAdminTrue}">
<Label Text="is_professional"></Label>
</StackLayout>
ViewModel Code
public void IsProfessional()
{
if (CurrentUser.IsAdmin!= true)
{
IsAdminTrue= false;
}
else
{
IsAdminTrue= true;
}
}
public void IsProfessional()
{
if (CurrentUser.IsAdmin!= true)
{
lblNo.Visibility= Visibility.Visible;
}
else
{
lblNo.Visibility = Visibility.Hidden;
}
}
Here lblNo is an label.
You should set the Visibility property on the Label, not the StackLayout. You could either change IsAdminTrue to IsAdminVisible (of type Visibility), or you could use the BooleanToVisibility converter. Second option is usually the preferred method.
Of course, none of this will matter if IsAdminTrue doesn't raise PropertyChanged.
I have a pop-up window containing a textbox that allows the user to type a number in, then click a button which closes the window. How can I bind what the user enters to a double variable in the code behind?
For example in my pop-up window I have:
<TextBox Text="{Binding ...., ElementName ....}" Margin="3" />
<Button Content="close" Click="Close_Popup" HorizontalAlignment="Center" />
I want to bind whatever is entered in that textbox to a variable called "double example;" in the code behind.
Is this the proper way to do it? Or will the variable go back to null when I close out of the window?
int anInteger;
anInteger = Convert.ToInt32(textBox1.Text);
Here's my xaml for the pop up window:
x:Name="this"
Title="Scale"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize">
<StackPanel Orientation="Vertical">
<TextBlock Text="Enter the distance between these two points in meters:" Margin="3"/>
<TextBox Text="{Binding userInput.get, ElementName=this}" x:Name="scaleText_Box" Margin="3"/>
<Button Content="Done" Click="closeScale_Window" Width="100" Margin="10"/>
</StackPanel>
Here's my code behind for the popup window:
public partial class ScaleInputWindow : Window
{
public ScaleInputWindow()
{
InitializeComponent();
}
public double userInput { get; set; }
private void closeScale_Window(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
}
Here's code from my main window.cs, userInput is a double declared at beginning.
ScaleInputWindow scaleInput = new ScaleInputWindow();
if (scaleInput.ShowDialog() == true)
{
userInput = scaleInput.Input;
}
You don't include the get
<TextBox Text="{Binding ElementName=this, Path=userInput}" x:Name="scaleText_Box" Margin="3"/>
And you could also just set the DataContext then no ElementName=this
DataContext is more commone
According to your code anInteger will be 0 as soon as you close the window
You can Declare a Static Variable in separate class in your project and Access it anywhere in project.
public class Popvalue
{
public static int anInteger = 0;
}
And on Your pop-up window assign the value to it.
Popvalue.anInteger = Convert.ToInt32(textBox1.Text);
I would suggest using the ShowDialog method to show your pop-up window.
In your pop-window code, create a property containing the value. (You can bind this to your TextBox.Text property)
public int anInteger {get; set;}
Then when the user closes the window (or clicks an ok button), set the DialogResult property (available to all windows)
private void btnDialogOk_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
Note that setting the DialogResult will automatically cause your popup window to close.
To use your window and get the result...
MyPopupWindow popup = new MyPopupWindow ();
if(popup.ShowDialog() == true)
enteredNumber = popup.anInteger;
I need to write a small application to read a configuration file and generate some report with it. I was hoping to finally use MVVM but it's quite tricky to get started. Oh, I'm using Caliburn.Micro framework.
So this is what I have, a shell (primary view that hosts other views) that has a ribbon with 3 buttons on it:
1) Open file
2) Show settings
3) Show results
And two other views, SettingsView and ResultsView with buttons to generate and delete a report.
So I guess the view structure would be like this:
ShellView
Ribbon
OpenFileButton
SettingsButton
ResultsButton
ContentControl (hosts SettingsView and ResultsView)
SettingsView
CalculateResultsButton
ResultsView
CancelResultsButton
The tricky part is this:
1. "Show settings" button is disabled until a file is opened (via Open file).
2. "Show results" button is disabled until a report is calculated (via a
method in SettingsViewModel).
3. If a report is calculated, the CalculateResultsButton is disabled and
CancelResultsButton is enabled and vice versa.
Please advise how could I achieve this ? I've no ideas what strategy should I go for. My non-MVVM-thinking-brain says that I should create a status variable and then somehow bind those buttons to that variable, but I guess that wont work in a MVVM world, right ? Any code example would be very very very appreciated!
Many thanks!
Since you're using CM you won't need any code-behind. You can delete the .xaml.cs files if you want.
This is a pretty basic example but it should give you an idea on how to control the state of the buttons. In this example, Open will be enabled and the other two are disabled. If you click on Open, Settings is enabled. The same happens with Results once Settings is clicked.
If you need a way to do global state the same concept can be applied by injecting a singleton, SharedViewModel, into the ViewModels and the CanXXX methods can check values in SharedViewModel. This is a SL demo of different things but one is injecting a singleton to share data, the same idea applies in wpf.
ShellView:
<Window x:Class="CMWPFGuardSample.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Orientation="Horizontal">
<Button x:Name="Open"
Content="Open" />
<Button x:Name="Settings"
Content="Settings" />
<Button x:Name="Results"
Content="Results" />
</StackPanel>
</Grid>
</Window>
ShellViewModel:
[Export(typeof (IShell))]
public class ShellViewModel : PropertyChangedBase, IShell
{
private bool _isOpen;
public bool IsOpen
{
get { return _isOpen; }
set
{
_isOpen = value;
NotifyOfPropertyChange(() => IsOpen);
NotifyOfPropertyChange(() => CanSettings);
}
}
private bool _isSettings;
public bool IsSettings
{
get { return _isSettings; }
set
{
_isSettings = value;
NotifyOfPropertyChange(() => IsSettings);
NotifyOfPropertyChange(() => CanResults);
}
}
public bool IsResults { get; set; }
public void Open()
{
IsOpen = true;
}
public bool CanSettings
{
get { return IsOpen; }
}
public void Settings()
{
IsSettings = true;
}
public bool CanResults
{
get { return IsSettings; }
}
public void Results()
{
}
}
MVVM and WPF Commands perfectly fits your "tricky part" requirements since have built in ICommand.CanExecute() method which allows enabling/disabling corresponding button based on custom logic.
To use this naice feature take a look first at the RoutedCommand Class and self explanatory example on MSDN How to: Enable a Command (see below code snippets).
And in general about MVVM, it is really SIMPLE! Just try it and you won't leave without it ;) In few words - you have to create for each EntityView.xaml corresponding EntityViewModel class and then just put instance of it in the View's DataContext either explicitly in code or using bindings:
var entityViewModel = new EntityViewModel();
var view = new EntityView();
view.DataContext = entityViewModel;
MVVM Command and Command.CanExecute bindings:
XAML:
<Window x:Class="WCSamples.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CloseCommand"
Name="RootWindow"
>
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
Executed="CloseCommandHandler"
CanExecute="CanExecuteHandler"
/>
</Window.CommandBindings>
<StackPanel Name="MainStackPanel">
<Button Command="ApplicationCommands.Close"
Content="Close File" />
</StackPanel>
</Window>
C# code behind:
// Create ui elements.
StackPanel CloseCmdStackPanel = new StackPanel();
Button CloseCmdButton = new Button();
CloseCmdStackPanel.Children.Add(CloseCmdButton);
// Set Button's properties.
CloseCmdButton.Content = "Close File";
CloseCmdButton.Command = ApplicationCommands.Close;
// Create the CommandBinding.
CommandBinding CloseCommandBinding = new CommandBinding(
ApplicationCommands.Close, CloseCommandHandler, CanExecuteHandler);
// Add the CommandBinding to the root Window.
RootWindow.CommandBindings.Add(CloseCommandBinding);