I need to make few buttons like this:
<fluent:Button
Size="Middle"
Visibility="{Binding Path=SomeTestingMethod}"
Command="{Binding Path=OtherMethod}" CommandParameter="PP"
Some Text</fluent:Button>
visible or not in case of "CommandParameter". I tried:
public Visibility SomeTestingMethod(object o)
{
return o.ToString == "something" ? Visibility.Visible : Visibility.Collapsed;
}
But compiler do not even check it. Also tried stuff like this:
private Visibility _someTestingMethod;
public Visibility SomeTestingMethod
{
get {
var commandExecutor = new RelayCommand(ButtonVisibility);
return _statusButtonsVisibility;
}
}
public void ButtonVisibility(object o)
{
_statusButtonsVisibility =
o.ToString == "something" ? Visibility.Visible : Visibility.Collapsed;
}
"SomeTestingMethod" is then reached but "ButtonVisibility" not.
I Have found other ways to reach visibility, but none of them alows me to get CommandParameter.How to do it correctly?
I have a few comments about the code presented.
First off, do you really want to make the button disappear if the user may not click it? I ask because the ICommand interface has a CanExecute() method which can hold logic to determine if the command may be executed. When a button is bound to a property that is an instance of an object implementing the ICommand interface, the button will automatically enable/disable itself based on the results of the CanExecute() logic. Note, that if that logic does something on a different thread, you may have to force a re-query of the command availability.
If you truly want the button to disappear rather than being disabled, as mentioned by #Jason Boyd in the comments, this is best accomplished by binding the visibility to a Boolean property in the view model and using a BooleanToVisibilityConverter to show/hide the button based on true/false of the property.
The view model should implement the INotifyPropertyChanged interface to communicate property changes to update the binding target.
Hopefully, that gives you a start in the right direction.
You can't get CommandParameter in property which you bind to Visibility property.
Get parameter in OtherMethod method and change SomeTestingMethod property.
Or you can use custom BoolToVisibility converter for using parametr.
Related
I'm trying to accomplish a correct MVVM architecture in WPF.
I have a player, in the "Model" section there is a Boolean property that says if we are "playing" right now.
public bool IsPlaying
{
get
{
return isPlaying;
}
set
{
isPlaying = value;
OnPropertyChanged("IsPlaying");
}
}
(Notice I implemented the "INotifyPropertyChanged" interface, so the OnPropertyChanged function reports the change)
in my ViewModel, I have a ImageSource property called "ToggleButtonIcon":
public ImageSource ToggleButtonIcon
{
get
{
if (Model.IsPlaying)
return pauseIcon;
else
return playIcon;
}
}
Which I bind to a "TogglePlayButton" in the view:
<cc:IconButton x:Name="TogglePlayButton"
Style="{StaticResource playButton}"
ImageSource="{Binding Path=ToggleButtonIcon,
UpdateSourceTrigger=PropertyChanged}"
Command="{Binding Path=TogglePlayCommand}"/>
(It's a custom control, but it's working, I checked)
Of course I want the button to change its image icon according to if it is playing (pause) and if it is not playing (play).
Problem is the ToggleButtonIcon does not notify when it changes, and I can't implement the INotifyValueChanged in the ViewModel section because a. I understood that's not a part of the MVVM architexture, and b. I don't know when it changes since it depends on the IsPlaying property of Model.
I thought about putting the ToggleButtonIcon property on the Model section, but that's not "Business Logic" so I don't think that's the right way.
I also thought about using a converter and bind the IconButton directly to "IsPlaying", which would probably work, but I read here: How can WPF Converters be used in an MVVM pattern? that you should not use converters at all in MVVM because you can do any convertion you want in the "ViewModel" Section.
What's the best way to accomplish this in MVVM architecture?
To me, IsPlaying should be in the ViewModel, with change notification implemented, as it represents an application state of sorts.
To solve the issue I would recommend taking the ToggleButtonIcon out of the ViewModel, and creating a DataTrigger on the IconButton control (via its Style), that binds to the IsPlaying property (on the ViewModel) and alters the ImageSource property based on that.
The model of MVVM should only hold class entities and those entities can on occasion have an INotiftPropertyChanged, but generally they do not.
What your intent though is that it is to convey a status and that should be on your viewmodel.
I would recommend that you have the status of IsPlaying be on the View Model (VM) and bind to that. Then in the command of TogglePlayCommand, it will set that property on the VM.
That way both items update propertly on a change to either. You can still new up your original object in the model and on the Setter of the VM's IsPlaying set the class instance property to its value if needed.
Take a look at my blog post Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding. Take note on how I use OnPropertyChanged to push change messages on other operations which can all for the flexibility you seek as well as having the viewmodel hold statuses and not the models.
You should put bool on class which implements interface INotifyPropertyChange:
Here an example:
public class Game : INotifyPropertyChanged
{
private bool _isPlaying;
public string IsPlaying
{
get { return _isPlaying; }
set {
_isPlaying = value;
this.NotifyPropertyChangedBool();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChangedBool()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs("IsPlaying"));
}
}
I am using MVVM. On my View I have a control that by default is hidden, it's Visibility property is Binded to ViewModels property.
<Grid>
<TextBox Visibility={Binding IsVisible, Mode=OneWay, Converter={StaticResource MyVisibilityConverter}}/>
<Grid>
In the ViewModel I have a property
private bool _isVisible;
bool IsVisible
{
get {return _isVisible;}
set {_isVisible = value; NotifyOfPropetyChanged(() => IsVisible);}
}
pretty much straighforward, to show the control I just do
IsVisible = true;
in my ViewModel and the TextBox becomes visible, works fine as intended.
What I want to do is to set Focus on the TextBox just after it becomes visible. The problem is that I can't find any good solution how to determine that this particular control just got visible and it is the moment I can set the focus.
The solution would be to test the visibility inside LayoutUpdated event, but it is definitely not the nicest thing to have in code.
Any better solution?
edit:
To clarify - I don't want to set the focus via MVVM from the ViewModel. There is no problem in setting the focus from the code-behind as it is the UI behaviour. The only problem is how to determine WHEN to do that. There is a some period of time beetween the ViewModel property is set and the layout being updated to match its state. After that perdiod of time I want to be able to catch anything that can notify me "my visibility has changed, now you can change focus"
You could use RegisterPropertyChangedCallback to register a change callback for the Visibility property of the textbox. then in the changed call back method you can set the focus is the visibility is visible.
Put this in the constructor of the code behind:
TextBox1.RegisterPropertyChangedCallback(UIElement.VisibilityProperty, VisibilityChanged);
and add the CallBack method:
private void VisibilityChanged(DependencyObject sender, DependencyProperty dp)
{
if (((UIElement)sender).Visibility == Visibility.Visible)
{
TextBox1.Focus(FocusState.Keyboard);
}
}
I am using the Telerik RadRibbonView in my WPF 4.5 project. The set up looks like this.
In my Shell I have a RibbonView and a TabControl defined as a regions called “RibbonRegion” and “TabRegion”. The RibbonRegion is basically the menu of the application and the TabRegion holds the main content.
I have also created a module with a View containing a RibbonTab and a RibbonButton. This button is hocked up to a command that sets the DataContext of a RibbonContextTabView and a TabItemView and registers them in their respective regions. The ContextTab and the TabItem is sharing the same ViewModel. This ViewModel has a propery “IsSelected” that the ContextTab and TabItem are bound to.
if (_regionManager.Regions["RibbonRegion"].Views.Any(v => v.GetType() == typeof(ContextTabView)) && _regionManager.Regions["TabRegion"].Views.Any(v => v.GetType == typeof(TabItemView)))
{
_regionManager.RequestNavigate("RibbonRegion", new Uri("ContextTabView", UriKind.Relative));
_regionManager.RequestNavigate("TabRegion", new Uri("TabItemView", UriKind.Relative));
}
else
{
ContextTabView contextTabView = _container.Resolve<ContextTabView>();
TabItemView tabItemView = _container.Resolve<TabItemView>();
contextTabView.DataContext = tabItemView.DataContext = new ContextTabTabItemViewModel();
_regionManager.RegisterViewWithRegion("RibbonRegion", () => contextTabView);
_regionManager.RegisterViewWithRegion("TabRegion", () => tabItemView);
}
The first time the Command above is executed the DataContext of the views is set and then they are registered in the regions. This also sets the “IsSelected” property to true. If I change focus to the RibbonTab my ContextTab and TabItem loses focus and the “IsSelected” propery is set to false. If I press the button again the RequestNavigate is executed and once again the property is set to true. Here is my problem. If I do this a third time nothing happens! The RequestNavigate is executed but the property is not set to true and the Views does not regain focus. I am fairly new to PRISM and I am afraid that I am way off here. Any help would be appreciated.
In order to keep communication between ViewModels in a loosely coupled manner, you could simply use the EventAggregator and raise an event from the Command Button implementation, which would be then handled by the TabItemViewModel.
The solution you mentioned by adding one ViewModel into another would not be ideal as these components would end up working with tight coupling and defining an incorrect situation as Views/ViewModels would not depend on another View.
Therefore, to accomplish the EventAgregation approach, you would need to receive the EventAggregator from the container throw constructor on the View/ViewModel where the button is clicked, and on each one of the ViewModels you would want to subscribe to that event setting the IsSelected property inside the EventHandler method.
You could subscribe to the "GiveFocusEvent" event and handle it on the ViewModels which would set their IsSelected property as shown below:
public TabItemViewModel(IEventAggregator eventAggregator, ..){
...
GiveFocusEvent setFocusEvent = eventAggregator.Get<GiveFocusEvent>();
setFocusEvent.Subscribe(SetFocusEventHandler, ThreadOption.UIThread);
}
public void SetFocusEventHandler(){
// change IsSelected property value..
}
The Event would be published from inside the Button's CommandHandler method as follows:
this.eventAggregator.GetEvent<GiveFocusEvent>().Publish();
Notice that you would need to create and make your "GiveFocusEvent" event class inherit from CompositePresentationEvent:
public class GiveFocusEvent : CompositePresentationEvent<string>{}
I hope this helped you,
Regards.
Hi I have a viewmodel where i can track the value of a certain item in the constructor. I am opening a dialog window using the MVVM model.
example
private int _myField;
public ClassName(int MyProperty)
{
_myField = MyProperty;
}
public int MyIntProperty
{
get{ return _myField;}
set { _myField = value;}
}
this is all perfect obviously.
but as soon as the window opens the value in the viewmodel changes.
lets say the _myField goes from 1 to 8 with out any interaction. i've walked through the code and there are no other interactions with the field.
also not in the code sample is the bound property.
anyone every came accross this before. it has me stumped.
Edit: included missing property from example
You should either:
1) Implement INotifyPropertyChanged on ClassName. This will allow you to raise the PropertyChanged event when you change MyIntProperty. WPF will listen to this event and update the UI accordingly.
or
2) Make ClassName inherit from DependancyObject and MyIntProperty a dependency property. This will take care of everything for you.
i`m trying to design a dialog window which will display different messages depending on what case will be true.
so for example when i have a
<CustomDialog Content="{Binding Path=Name}"/>
is there a possibility to change the Binding Path=Name to Path=Whatever or how do you implement something like that ? When the Control should use other resources on runtime.
--------------edit
I ok i`ll try to describe my problem better ;)
I have an ResourceDictionary with strings for example
<System:String x:Key="Message1">Message1</System:String>
<System:String x:Key="Message2">Message2</System:String>
<System:String x:Key="Message3">Message3</System:String>
So when I now call my UserControl
Doing it customdialog.visibility = true; for example
<CustomDialog Text=”” />
I want to define which key from the resourcedictionary is taken when the dialog popups up.
something like customdialog.text = Message1; but Loaded from the ResourceDictionary
is that possible or is there an better way of doing something like this ?
You may provide another content to the same property Name at runtime in code-behind. Suppose you have Initialize (or may be Show) method in your CustomDialog and the last one implements INotifyPropertyChanged:
public class CustomDialog : INotifyPropertyChanged
{
//Your implementation of class goes here
public void Initialize(string message)
{
Name = message;
Visibility = Visibility.Visible;
}
public string Name
{
get {return _name;}
set
{
if (_name != value)
{
_name = value;
raiseOnPropertyChanged("Name");
}
}
}
//Your implementation of class goes here
}
In method Initialize there will be updated Name property and your control will be shown. When there will be setting of Name property must be raise PropertyChanged event which will tell presentation that binded value has updated and to reflect it in the UI.
The easiest way I can think of would be to bind to the parent item, not to a child property, and then use a DataTemplateSelector to select a different template at run-time, depending on some condition involving the bound object or its properties.
Alternatively, if the Content has well defined types, you only need to define DataTemplates with specific data types, and they will be automatically used to display objects of those types.
Not knowing more about the context I can't be much more specific, but if you search for more information on DataTemplates and DataTemplateSelectors you should be fine - you can find a lot of useful information here.