Binding a button in WPF using PRISM DelegateCommand - wpf

I am just starting to learn WPF using the PRISM library. Please help me to solve one question. How to bind a button using DelegateCommand?
In the code behind everything is fine. In the MVVM code I can't get it to work.
Thanks.
My code in XAML:
<UserControl x:Class="Panel.Modules.DockingManager.DockingManagerPanel.DockingManagerPanelPrism"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Button x:Name="button"
Command="{Binding LoadAllFiles}"
CommandTarget="{Binding ElementName=textBox}"
Height="24" Width="24">
</Button>
<TextBox x:Name="textBox" </TextBox>
</Grid>
</UserControl>
And my code in ViewModel:
public class DockingManagerPanelViewModel : BindableBase
{
public DelegateCommand LoadAllFiles { get; set; }
public DockingManagerPanelViewModel()
{
LoadAllFiles = new DelegateCommand(LoadTxt);
}
private void LoadTxt()
{
var dialog = new Microsoft.Win32.OpenFileDialog();
dialog.Multiselect = true;
dialog.FileName = "Document";
dialog.DefaultExt = ".txt";
dialog.Filter = "Text documents (*.*)|*.*";
bool? result = dialog.ShowDialog();
if (result == true)
{
StreamReader sr = new StreamReader(File.OpenRead(dialog.FileName));
sr.ReadToEnd();
}
}
}

You are reading the text but not doing anything with what you read. You need to read it into some sort of string property and then bind the TextBox to that property.
Also you are failing to use a using statement on the StreamReader.
The good news is that you don't need to use StreamReader. You can just use the much simpler File.ReadAllText function.
First, add this to your view model. A public property exposing the read text. It uses BindableBase's SetProperty to raise the proper event when the value changes
public string LoadedText
{
get => _loadedText;
set => SetProperty(ref _loadedText, value);
}
private string _loadedText = string.Empty;
Next, change the last few lines of your LoadTxt function to do this instead of using StreamReader
if (result == true)
LoadedText = File.ReadAllText(dialog.FileName)
Finally, change the TextBox in XAML to look like this. I made it one way becase I assume you do not want the user editing inside of it, to change the value in the backing LoadedText property. A better approach might be to just use a TextBlock instead of a TextBox
<TextBox x:Name="textBox" Text="{Binding LoadedText, Mode=OneWay}"/>

Related

How to show floating virtual keyboard (user control) in MainWindow when an input control (from another user control) has been set to focus in WPF?

I have been doing development work in WPF application which uses an MVVM pattern for a couple of days now. I'm very new to WPF and MVVM pattern as well.
In my scenario, I have a user control view (named EPayView.xaml) which has a textbox that will accept a phone number. The view has a corresponding viewmodel (named EPayViewModel.cs). In the MainWindow.xaml, I have a user control (floating virtual keyboard) which is derived from namespace controls WpfKb.Controls. The MainWindow.xaml also has a corresponding viewmodel (named MainViewModel.cs)
Having said that, I have done research on how to use attached dependency properties which lead me to this solution. Set focus on textbox in WPF from view model (C#) which I believe this is where I could bind the property IsFocused in the textbox of EPayView.xaml.
Below are the codes that I have already incorporated in my solution.
EpayView.xaml (textbox xaml markup)
<TextBox Text="{Binding PhoneNo}" Grid.Row="5" Margin="10,0,10,0" VerticalContentAlignment="Center" FontSize="12" x:Name="Email" behaviors:FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"/>
MainWindow.xaml (xaml markup)
<Window x:Class="SmartPole540.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb"
xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl"
xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core"
xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core"
xmlns:square="clr-namespace:SmartPole.View.Square;assembly=SmartPole.View"
xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View"
Title="CitiPulse"
WindowStartupLocation="Manual"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
Name="mainWindow">
<userControls:RollPanel.BottomContent>
<square:SquareView Canvas.Top="1010" DataContext="{Binding DataContext.SquareViewModel,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type userControls:RollPanel}}}"/>
</userControls:RollPanel.BottomContent>
<controls:FloatingTouchScreenKeyboard
x:Name="floatKb" Width="500" Height="250" PlacementTarget="{Binding ElementName=MainGrid}"
Placement="Center" AreAnimationsEnabled="False" Visibility="Visible"
IsOpen="{Binding IsChecked, ElementName=kbButton}"/>
</Window>
In the above code, the user control RollPanel.BottomContent host the EPayView.xaml view inside another view which is RollPanel.xaml
EpayViewModel.cs contains the static class FocusExtension for the IsFocused attached property (refer to this solution - Set focus on textbox in WPF from view model (C#)). And, EPayViewModel.cs already implemented INotifyPropertyChanged which is wrapped inside a concrete class ObservableObject that accepts type of T. This is also same with MainViewModel.cs
public class EPayViewModel : ObservableObject<EPayViewModel>, IPaymentViewModel, IActiveViewModel
{ ... }
public class MainViewModel : ObservableObject<MainViewModel>
{ ... }
As such, my goal is that when the textbox in EPayView.xaml has the focus, the floating virtual keyboard (floatKb) in the MainWindow.xaml will be shown.
I'm stuck on how to proceed (I was thinking if a call to FocusExtension static class in EPayViewModel inside my MainViewModel.cs will suffice?), any help is greatly appreciated.
Cheers,
As AnjumSKhan already said, to react to some event in a MVVM way, you'll have to use Command. Command can be called within an EventTrigger, you will need to add a Reference to System.Windows.Interactvity component.
Let's assume you have a simple View and View Model and you need to show this View when the TextBox in a MainWindow got focus.
View (NewWindow.xaml)
<Window x:Class="My.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<TextBlock Text="{Binding Message}"/>
View Model
public class NewWindowViewModel
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; }
}
}
You also have a MainWindow, it is a main view for an app and it contains the target TextBox. You may see that there is an EventTrigger added to the TextBox and it has a property InvokeCommandAction which is binded to the MainWindowViewModel's command called ShowCommand.
Main Window
<Window x:Class="My.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" Title="MainWindow" Height="350" Width="525">
<TextBox Height="40" Text="{Binding Text}">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="GotFocus">
<Interactivity:InvokeCommandAction Command="{Binding ShowCommand}"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</TextBox>
In the Show method of MainWindowViewModel NewWindow view is created and got new NewWindowViewModel instance as a DataContext. RelayCommand class is presented in my answer to this question
MainWindowViewModel
public class MainWindowViewModel
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; }
}
private ICommand _increaseCommand;
public ICommand ShowCommand
{
get
{
if (_increaseCommand == null)
{
_increaseCommand = new RelayCommand(
p => true,
Show);
}
return _increaseCommand;
}
}
private void Show(object obj)
{
var w = new NewWindow();
var nvm = new NewWindowViewModel();
nvm.Message = "Test";
w.DataContext = nvm;
w.Show();
}
}
What is left is to create a new MainWindowViewModel and setup a DataContext for MainWindow.
public MainWindow()
{
InitializeComponent();
var mvm = new MainWindowViewModel();
mvm.Text = "Focus me!";
DataContext = mvm;
}
Hope it will help.

Data Binding doesn't work in xaml

I try to use binding to display Hi in the Text content.
However, when clicking the button, it doesn't work.
Could someone help me to solve the problem?
Thanks.
1.XAML CODE :
<Window x:Class="Wpftest.binding.Window0"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window0" Height="300" Width="300">
<Grid>
<TextBox x:Name="textBox2" VerticalAlignment="Top" Width="168"
Text="{Binding Source= stu, Path= Name, Mode=TwoWay}"/>
</Grid>
</Window>
2.Class :
namespace Wpftest.binding.Model
{
public class student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
public string Name
{
get { return name; }
set { name = value;
if(this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new
PropertyChangedEventArgs("Name"));
}
}
}
}
}
3.XAML.cs:
namespace Wpftest.binding
{
public partial class Window0 : Window
{
student stu;
public Window0()
{
InitializeComponent();
stu = new student();
}
private void button_Click(object sender, RoutedEventArgs e)
{
stu.Name += "Hi!";
}
}
}
There are many ways to achieve what you need; the correct method depends very much on what style of application you want to create. I'll demonstrate two methods that will require minimal changes from your supplied example:
Method 1
Set the DataContext to stu and bind to the Name property.
XAML.cs
private student stu;
public Window0()
{
InitializeComponent();
stu = new student();
DataContext = stu;
}
XAML code
<TextBox Text="{Binding Path=Name, Mode=TwoWay}"/>
Method 2
Generally you will set the DataContext to some object other than the Window (e.g. the ViewModel if you are following the MVVM pattern), but sometimes you may need to bind a control to some property of the Window. In this case the DataContext can't be used, but you can still bind to a property of the Window by using RelativeSource. See below:
XAML.cs
// note this must be a property, not a field
public student stu { get; set; }
public Window0()
{
InitializeComponent();
stu = new student();
}
XAML code
<TextBox Text="{Binding Path=stu.Name, Mode=TwoWay,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
Hint: if you are having trouble with WPF data binding, then it often helps to look at the debugger output window to see the binding trace messages. And debugging can be further enhanced by adding this namespace to the Window element
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
and then setting the TraceLevel e.g.
<TextBox Text="{Binding Source=stu, diag:PresentationTraceSources.TraceLevel=High}"/>
Basically you need to set DataContext property to your Window.
For example:
public MainWindow()
{
DataContext=new YourViewModel();
}
DataContext of Window is a way to communicate between View(XAML) and ViewModel(C# code)
In addition, you can add DataContext in xaml:
<Window.DataContext>
<local:YourViewModel/>
</Window.DataContext>
Also, instead of handling Click event, you should use Command property of Button. Example can be seen here.

Bind TextBox.TextProperty to a property of type Binding from the Model

I've got a list of command buttons (with input) I want to bind with the model.
The thing is I want the textbox in the button to bind to somewhere (see viewmodel).
The following code is what I tried and failed. Is it (even) possible to set binding on the model then bind this to a control?
Or in other words am I trying to do something the stupid way?
View:
<ToolBar Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" ItemsSource="{Binding SelectedTab.Commands}" Height="34">
<ToolBar.Resources>
<DataTemplate DataType="{x:Type model:ZoekCommandButtons}">
<Button Command="{Binding Command}" ToolTip="{Binding Tooltip}" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image, Converter={StaticResource ImageConv}}" Height="16" Width="16"></Image>
**<TextBox Width="100" Text="{Binding Text}">**
<TextBox.InputBindings>
<KeyBinding Gesture="Enter" Command="{Binding Command}"></KeyBinding>
</TextBox.InputBindings>
</TextBox>
</StackPanel>
</Button>
</DataTemplate>
</ToolBar.Resources>
</ToolBar>
Model:
public class ZoekCommandButtons : BaseModel, ICommandItem
{
private string _header;
private string _image;
private bool _isEnabled;
private Visibility _isVisible;
private ICommand _command;
private string _tooltip;
private Binding _text;
public Binding Text
{
get { return _text; }
set { _text = value; OnPropertyChanged("Text"); }
}
(etc)
Viewmodel:
Commands.Add(new ZoekCommandButtons()
{
Image = "search.png",
IsEnabled = true,
**Text = new Binding { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(UserControl), 1), Path = new PropertyPath("FilterText") },**
Command = FilterCommand,
Tooltip = "Zoeken",
Header = "Zoeken"
});
First off, I would not recommend exposing Binding as a ViewModel property; in this particular case, it sounds more to me like you have nested ViewModels, and that approach would be far more suitable - that is, you have a "MamaViewModel" that has your "Commands" property, which is in turn a collection of "CommandButtonViewModels"...
Ok, That said...you can do this, although I must reiterate that you probably should not; what you're missing is "something to evaluate the Binding on" to provide a value. Here's a class that gives you that:
public static class BindingEvaluator
{
// need a DP to set the binding to
private static readonly DependencyProperty PlaceholderProperty =
DependencyProperty.RegisterAttached("Placeholder", typeof(object), typeof(DependencyObject), new UIPropertyMetadata(null));
// Evaluate a binding by attaching it to a dummy object/property and evaluating the property value
public static object Evaluate(Binding binding)
{
var throwaway = new DependencyObject();
BindingOperations.SetBinding(throwaway, PlaceholderProperty, binding);
var retVal = throwaway.GetValue(PlaceholderProperty);
return retVal;
}
}
That, combined with a ViewModel definition something like:
public class DontDoThisViewModel
{
public Binding TextBinding {get; set;}
public string Text
{
get
{
return BindingEvaluator.Evaluate(TextBinding) as string;
}
}
}
Should work...here's a test app I threw together in LINQPad:
void Main()
{
var wnd = new Window() { Title = "My window" };
var text = new TextBlock();
text.Text = "Hopefully this shows the window title...";
text.SetBinding(TextBlock.TextProperty, new Binding("Text"));
wnd.Content = text;
var vm = new ViewModel();
var vmBinding = new Binding("Title");
vmBinding.Source = wnd;
vm.TextBinding = vmBinding;
wnd.DataContext = vm;
wnd.Show();
}
AGAIN, I must strongly recommend you NOT do this...but I was curious, so I had to come up with a way. ;)
Ok. I wasn't thinking straight.
Changed the Text property in the Model to string and handled the command with this property.
(although it would be nice to set binding on the model somehow...)

Changing the background color of a textblock in MVVM-WPF (and retaining it)

Here is the working code i have: The text and background color property do change when I click the button (but for a micro second) and are then set back to the default text/color. Seems like RaisePropertyChanged is being triggered again and again. Can somebody help point what I am doing wrong?
MainWindow.xaml code
<Window x:Class="BuiltIn_Custom_Commands_Eg.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="{Binding txtblck_text, StringFormat=Default: {0}}" Padding="10" FontStyle="Italic" Background="{Binding txtblck_color}"/>
<Button Content="Change Color" Width="100" Height="30" Margin="20" Command="{Binding OkCommand}" />
</StackPanel>
</Grid>
ViewModel Code:
class Example_ViewModel : ViewModelBase
{
#region Properties
private string _txtblck_text;
private Brush _txtblck_color;
public ICommand OkCommand {get; set;}
public string txtblck_text
{
get { return _txtblck_text; }
set
{
_txtblck_text = value;
RaisePropertyChanged("txtblck_text");
}
}
public Brush txtblck_color
{
get { return _txtblck_color; }
set
{
_txtblck_color = value;
RaisePropertyChanged("txtblck_color");
}
}
#endregion
#region Constructor
public Example_ViewModel()
{
OkCommand = new myCommand(myOkExecute, myCanOkExecute);
}
#endregion
private void myOkExecute(object parameter)
{
txtblck_color = Brushes.CadetBlue;
//RaisePropertyChanged("txtblck_color");
txtblck_text = "You Clicked me!!!";
//RaisePropertyChanged("txtblck_text");
}
private bool myCanOkExecute(object parameter)
{
txtblck_color = Brushes.Yellow;
txtblck_text = "You havent clicked me!!!";
return true;
}
}
The CanExecute method will and should be called whenever bindings change. Therefore changing a binding in the Execute method (color) will cause CanExecute to be called again.
Instead, why dont you initialize the colors private member once in the constructor as follows.
public Example_ViewModel()
{
OkCommand = new myCommand(myOkExecute, myCanOkExecute);
_txtblck_color = = Brushes.Yellow;
}
Note, the same is also true for the text property. Normally all property private member should be set up with defaults on initialize (constructor) as this avoids unnecessary calls to INotifyPropertyChanged.
Also, in order to test how the code is behaving and to confirm this just set some breakpoints in the CanExecute method to see how the program flow is behaving.
Your problem is that you shouldn't do any setting of your properties in your myCanOkExecute...because it is that that is being called and changing your properties back to the yellow, etc.
The CanExecute methods of Commands could be called multiple times and sometimes when you don't expect ...e.g. when the focus changes to a different control, when certain controls are being edited/sent keypress, after a Command has been executed, when someone calls CommandManager.InvalidateRequerySuggested, etc.
Thus what's happening is your myCanOkExecute is being called shortly after you have clicked and executed your button.

Binding a Textbox to a property in WPF

I have a Textbox in a User Control i'm trying to update from my main application but when I set the textbox.Text property it doesnt display the new value (even though textbos.Text contains the correct data). I am trying to bind my text box to a property to get around this but I dont know how, here is my code -
MainWindow.xaml.cs
outputPanel.Text = outputText;
OutputPanel.xaml
<TextBox x:Name="textbox"
AcceptsReturn="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Text="{Binding <!--?????--> }"/> <!-- I want to bind this to the Text Propert in OutputPanel.xmal.cs -->
OutputPanel.xaml.cs
namespace Controls
{
public partial class OutputPanel : UserControl
{
private string text;
public TextBox Textbox
{
get {return textbox;}
}
public string Text
{
get { return text; }
set { text = value; }
}
public OutputPanel()
{
InitializeComponent();
Text = "test";
textbox.Text = Text;
}
}
}
You have to set a DataContext in some parent of the TextBox, for example:
<UserControl Name="panel" DataContext="{Binding ElementName=panel}">...
Then the binding will be:
Text="{Binding Text}"
And you shouldn't need this - referring to specific elements from code behind is usually bad practice:
public TextBox Textbox
{
get {return textbox;}
}
I hope this example will help you.
1) Create UserControl.
2) Add to XAML <TextBlock Text="{Binding Path=DataContext.HeaderText}"></TextBlock>
3) In the code behind of that UserControl add
public partial class MyUserControl: UserControl
{
public string HeaderText { set; get; } // Add this line
public MyUserControl()
{
InitializeComponent();
DataContext = this; // And add this line
}
}
4) Outside of the control and let's say in the MainWindow Load event you have to do like
this.gdMain = new MyUserControl{ HeaderText = "YES" };
If your are starting to bind properties I suggest you check some articles on MVVM.
This is a very powerful architecture you can use on WPF. I found it very useful in my projects.
Check this one.

Resources