Change buttons BackgroundColor using MVVM - c#

I have two buttons, When I am clicking on one button I want to change another button BackgroundColor using mvvm
I am trying to make it like this
<StackLayout>
<Button Text="Red" BackgroundColor="Accent" Command="{Binding ChangeButtons}" x:Name="btnRed"></Button>
<Button Text="Blue" x:Name="btnBlue"></Button>
</StackLayout>
When I click on btnRed I want btnBlue BackgroundColor to be changed.
ModelView page
public class ButtonColorViewModel
{
public Command ChangeButtons
{
get
{
return new Command(() => {
//Change here button background colors
});
}
}
}
How can I implement it?

Basically like this, you need a color property to bind to. You can change the value of the property in your command. You can adjust it to suit your needs,2,3, colors/buttons etc.:
public class ButtonColorViewModel
{
public Command ChangeButtons
{
get
{
return new Command(() => {
//Change here button background colors
BackgroundColorBtn1 = Color.Green; //or something
});
}
}
private _backgroundColorBtn1 = Color.White;
public Color BackgroundColorBtn1
{
get { return _backgroundColorBtn1;}
set
{
if (value == _backgroundColorBtn1)
return;
_backgroundColorBtn1 = value;
NotifyOnPropertyChanged(nameof(BackgroundColorBtn1));
}
}
}
With XAML:
<StackLayout>
<Button Text="Red" BackgroundColor="Accent" Command="{Binding ChangeButtons}"
x:Name="btnRed"></Button>
<Button BackgroundColor="{Binding BackgroundColorBtn1}" Text="Blue"
x:Name="btnBlue"></Button>
</StackLayout>

The best option, depending on your thought about MVVM, is to avoid (as much as possible) using UI inside ViewModel.
In the previous answer, the color object is used.
If you want to keep MVVM in this case:
create a property with OnPropertyChanged like maybe an enum
Bind it to background color button
Use a converter to convert your property into background color :)
I know it's more stuff but it's more MVVM like.
Also, if you stay only in UI, go to code behind.
Use click and manage the background color directly.

Related

Setting the colour of a LiveCharts Series that has custom labels using XAML Binding

The typical way of setting the Series Fill and Stroke are explained perfectly on the Livecharts website. However, in order to set custom labels for points you need to create the Series in the view model (Shown below). This prevents you from being able to call Fill or Stroke in the XAML as you don't have each series being created like the example below.
<lvc:CartesianChart Name="Chart" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="15">
<lvc:CartesianChart.Series>
<lvc:LineSeries Values="{Binding Values}" LineSmoothness="1" StrokeThickness="10"
DataLabels="True" FontSize="20" Foreground="#6B303030"
Stroke="White" Fill="Transparent" PointGeometrySize="0"/>
</lvc:CartesianChart.Series>
My current code which creates the series and its associated labels.
ViewModel
ABValuesSC = new SeriesCollection
{
new LineSeries
{ Values = ABValues,
DataLabels = true,
FontSize = 14,
//MinPointShapeDiameter = 15,
StrokeDashArray = new System.Windows.Media.DoubleCollection {2},
Fill = System.Windows.Media.Brushes.Transparent,
LabelPoint = point =>
{if(point.Key==0)
{
return "A";
}
else
{
return "B";
}
}
},
new ScatterSeries
{ Values = TriggerValues,
DataLabels = true,
FontSize = 14,
MinPointShapeDiameter = 15,
LabelPoint = point =>
{if(point.Key==0)
{
return "1";
}
else
{
return "2";
}
}
},
new LineSeries
{ Values = NAVmatValues,
LineSmoothness=0,
}
};
XAML
<lvc:CartesianChart Series="{Binding ABValuesSC}"/>
Giving you this output.
Is there a method for accessing a series fill for the chart to change it from the default and have it be bindable? for example would it be possible to have the colours be bound to a list
or is there a better way of making the labels for my chart such that i can use a similar method to the example at the top of this post?
Instead of creating a SeriesCollection programmatically and bind it to the View, its possible to define (most) of these Things directly in the XAML and only bind the Things you need to change in your ViewModel.
Move to XAML
As far as I understood your code, you only want to change the Values and the Fill in your ViewModel, so we put your "configuration" in the XAML which looks something like this:
<lvc:CartesianChart>
<lvc:CartesianChart.Series>
<lvc:LineSeries Values="{Binding ABValues}" DataLabels="True" FontSize="14" StrokeDashArray="1,1" Fill="{Binding ABColor}" LabelPoint="{Binding ABLabelPoint}"/>
<lvc:ScatterSeries Values="{Binding TriggerValues}" DataLabels="True" FontSize="14" MinPointShapeDiameter="15" LabelPoint="{Binding TriggerLabelPoint}"/>
<lvc:LineSeries Values="{Binding NAVmatValues}" LineSmoothness="0"/>
</lvc:CartesianChart.Series>
</lvc:CartesianChart>
LabelPoint Binding
The LabelPoint cant be set (or at least i dont know how) in the XAML and must be provided as a Property in your ViewModel (see Code below)
class YourClass
{
//Property to Bind
public Func<ChartPoint,string> ABLabelPoint { get; set; }
//Constructor
public YourClass()
{
//Define LabelPoint, where 0 = A, 1 = B etc.
//Or use your Code, doesent really matter
ABLabelPoint = point => ((char)(point.X + 65)).ToString();
}
}
(Dont forget to do this for the Scatterseries LabelPoint as TribberLabelPoint Property)
Values Binding
The Values are now bound, therefore you must expose them as a Property like this
public ChartValues<ValueType> ABValues { get; set; }
Note: Replace ValueType with the used Type, eg. int or byte.
Fill Color Binding
Like the Values, the Fill color is bound to a Property which must be implemented. Make sure you notify the View when the Color Changes (see INotifyPropertyChanged)
If your class already has this interface implemented it could look like this
//private Field
private SolidColorBrush _abColor = new SolidColorBrush(Colors.Green);
//Public Property which the XAML binds to
public SolidColorBrush ABColor
{
get { return _abColor; }
set
{
_abColor = value;
OnPropertyChanged();
}
}
As you already use MVVM, use a command to manipulate color. In the Command delegate all you have to do is to access the series collection and pick out the series you want to change. Note that you have to cast it to the right series type.
((LineSeries)ABValuesSC [0]).Fill = Brushes.Aqua; //change fill of first series
This way you can manipulate any property of the series you want, not just fill.

label Hide and show using xaml and mvvvm

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.

Changing isVisible property of Xamarin Forms XAML buttons

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;
}

Can I access XAML elements in an array in the codebehind?

I've been looking around but I haven't been able to find anything on this. I am trying to get started making Windows 8.1 apps in C# with Visual Studio 2013 Pro. I want to be able to access multiple elements (particularly buttons or text blocks) in an array because this is more convenient for developing things like board games. For instance, if I were developing tic-tac-toe, I might use a series of buttons like this:
<Grid>
<Button Name="Cell00"/>
<Button Name="Cell01"/>
<Button Name="Cell02"/>
<Button Name="Cell10"/>
<Button Name="Cell11"/>
<Button Name="Cell12"/>
<Button Name="Cell20"/>
<Button Name="Cell21"/>
<Button Name="Cell22"/>
<Grid/>
Now for the function that would check for a win, I would have to check all possible combinations like this is in the code behind:
private bool CheckForWin()
{
if((Cell00 == Cell01) && (Cell01 == Cell02) && isNotBlank(Cell02)) return true;
if((Cell10 == Cell11) && (Cell11 == Cell12) && isNotBlank(Cell12)) return true
...
return false; //if none of the win conditions pass
}
This type of code would be extremely cumbersome. I would like to write it instead in a way that lets me check the array with for loops.
I realize that with tic-tac-toe, it is fairly easy to code it using brute force, but this was the first example that came to my head. Other games like Reversi or Go would not work well like this because of either the sheer size or the fact that pieces placed can change other cells than the one they were placed on.
Any help with this would be greatly appreciated.
This is not the correct way to use WPF. WPF is designed to use data binding....creating and manipulating UI elements directly is bad form. There are more posts/discussion/questions about this than you can imagine and I'll leave you to research them for yourself. In the mean time this is how you use WPF "properly":
First use NuGet to add MVVM lite to your project so that you get the ViewModelBase class and create a view model for a single cell:
public class Cell : ViewModelBase
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; RaisePropertyChanged(() => this.Text); }
}
}
One level up you'll want a main model to encapsulate an array of these, this is where you will typically do all your game logic:
public class MainModel : ViewModelBase
{
private ObservableCollection<Cell> _Cells;
public ObservableCollection<Cell> Cells
{
get { return _Cells; }
set { _Cells = value; RaisePropertyChanged(() => this.Cells); }
}
public MainModel()
{
this.Cells = new ObservableCollection<Cell>(
Enumerable.Range(1, 100)
.Select(i => new Cell { Text = i.ToString() })
);
}
}
Notice that all I'm doing at the moment is creating a 100-element collection of cells. This main view model becomes the one that you assign to your window's data context:
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainModel();
}
Now your XAML controls need to bind to this data. ItemsControl is used to render a collection of elements so use one of those and bind it to your array. You want them displayed in a 2D grid so replace the ItemsPanelTemplate with a WrapPanel. Finally add a DataTemplate for your Cell class so that a button gets drawn for each cell:
<Window.Resources>
<DataTemplate DataType="{x:Type local:Cell}">
<Button Width="32" Height="32" Content="{Binding Text}"/>
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Cells}" Width="320" Height="320" HorizontalAlignment="Center" VerticalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
That's how you use WPF. Your logic is stored entirely in the view model and it's completely decoupled from the view. Here's what this particular code displays, it should be pretty self-evident how flexible this code is and easy to change:
That's very possible. Simply declare an array variable :
private Button[] _buttonArray;
populate the array once, maybe in constructor :
_buttonArray = new[] {Cell00, Cell01, .... , Cell22};
And all of the buttons are now accessible through _buttonArray.

Passing application state between viewmodels in MVVM WPF application

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);

Categories