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);
}
}
Related
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.
I am trying to get my little program to do some calculations.
So far I have 15 textboxes, named TxtPP(followed by product type),so i got TxtPPproduct1, TxtPPproduct2 etc....
At the bottom of the form I have a disabled textbox which shows the total of all the above textboxes.
I don't want to use a button to do the calculations, I want this to be done every time a value is added to one of the textboxes (so on LostFocus).
Is there a clean way to do this?
To do this, you need to take advantage of set being a method, which means you can raise PropertyChanged for other properties than just the one you are in.
First you need each source textbox to be bound. To get it to update the source on losing input focus, set the UpdateSourceTrigger to LostFocus for example:
<TextBox Text="{Binding FirstSourceValue, UpdateSourceTrigger=LostFocus}"/>
Now, in the setter for the bound member, you need to raise PropertyChanged for the derived value as well, something like:
public double FirstSourceValue
{
get { return firstSourceValue; }
set
{
firstSourceValue = value;
NotifyPropertyChanged(); //Notify for this property
NotifyPropertyChanged("DerivedValue"); //Notify for the other one
}
}
And the derived value property just returns the result of the calculation:
public DerivedValue
{
get { return FirstSourceValue + SecondSourceValue; }
}
Now you can bind your disabled text box to it, and it will update whenever the other text boxes do:
<TextBox IsEnabled="False" Text="{Binding DerivedValue, Mode=OneWay}"/>
I am using MVVM Light WPF 4.
I have a ContentPresenter in my Home.xaml.
<ContentPresenter Name="MDI" Content="{Binding WindowName, Mode=OneWay}">
I am binding user control to this in viewmodel like
public UserControl WindowName { get; set; }
void ShowSalesEntry()
{
WindowName = null;
WindowName = new SalesEntry();
RaisePropertyChanged("WindowName");
}
by using command in a menu click and it is binding fine.
Now in the user control i have a button which i used to close (but to close i change the visibility
to collapsed) by this way..
Visibility="{Binding visibility, Mode=OneWay}"
in the user control view model,
public SalesEntryViewModel()
{
visibility = Visibility.Visible;
cmdExitWindow = new RelayCommand(ExitWindow);
RaisePropertyChanged("visibility");
}
and the following to close (visibility to collapsed)
public RelayCommand cmdExitWindow { get; set; }
void ExitWindow()
{
visibility = Visibility.Hidden;
RaisePropertyChanged("visibility");
}
To exit (means visibility collapsed)..
This is working fine upto this.
Problem is when i click the same page i mean to show the same user control,
now this time the visibility is still collapsed. Even though i changed to visible in the
load event.
How to solve this..
I am new to MVVM WPF.. Please help me..
Problem is when i click the same page i mean to show the same user
control, now this time the visibility is still collapsed. Even though
i changed to visible in the load event.
Based on this comment and the code provided, you've either omitted code, or you've confused the purpose of the constructor.
In your constructor, you have set the Visibility to Visible. You then have a method that sets the Visibility to Hidden, but there is nothing to ever set it back to Visible once this has occurred. The constructor only fires when the object is created. You need something to set the Visibility back at the appropriate time (ie. your comment "when i click the same page").
//Add these lines to the method/event that will show the control again
visibility = Visibility.Visible;
RaisePropertyChanged("visibility");
That's the best answer I can give based on what you've provided.
I have a ComboBox in a WPF app that has recently been refactored to use the MVVM pattern. An apparent side effect to this change is that changing focus to another application while the combobox dropdown is visible completely prevents the dropdown from being visible again, until the app has been restarted.
The ComboBox DataContext is set to my ViewModel, with its ItemsSource bound to an ObservableCollection<String> SearchSuggestions, and IsDropdownOpen bound to a property SuggestionsVisible in the ViewModel.
The desired effect is a search box with autocomplete suggestions. It should close if there are no suggestions in the ObservableCollection, if the user cancels the search, if the user runs the search, or if the user clicks away from the text field - either inside the app or outside it.
The ViewModel explicitly sets the SuggestionsVisible property to true or false based on whether SearchSuggesions contains any items after user input. This process continues to take place after this bug manifests itself, just with no visible change to the UI. Any idea why losing focus while the dropdown is open renders the dropdown un-openable for the rest of the app's session?
Here's how I have things wired together:
<ComboBox DataContext="{Binding SearchBoxVm}" Name="cmboSearchField" Height="0.667"
VerticalAlignment="Top" IsEditable="True" StaysOpenOnEdit="True"
PreviewKeyUp="cmboSearchField_OnKeyUp"
PreviewMouseLeftButtonUp="cmboSearchField_OnPreviewMouseLeftButtonUp"
Background="White" ItemsSource="{Binding SearchTopics}"
IsDropDownOpen="{Binding SuggestionsVisible,
UpdateSourceTrigger=PropertyChanged}"
Margin="50.997,15.333,120.44,0"
RenderTransformOrigin="0.5,0.5" Grid.Row="1" >
<!-- SNIP STYLING -->
</ComboBox>
ViewModel:
public class SearchBoxViewModel : INotifyPropertyChanged
{
public void ResetSearchField(bool preserveContents = false)
{
if (!preserveContents || string.IsNullOrEmpty(Query))
{
Foreground = Brushes.Gray;
QueryFont = FontStyles.Italic;
Query = DEFAULT_TEXT;
}
}
public bool OnKeyUp(Key key)
{
bool showDropdown = SuggestionsVisible;
bool changeFocusToCombobox = false;
if (keyInValidRange(key))
{
SearchSuggestions = GetSearchSuggestions(Query);
if (SearchSuggestions.Count > 0)
{
SuggestionsVisible = true;
}
}
return changeFocusToCombobox;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
bool _suggestionsVisible = false;
public bool SuggestionsVisible
{
get { return _suggestionsVisible; }
set
{
// this section is still called after this issue manifests,
// but no visible change to the UI state is made
_suggestionsVisible = value;
NotifyPropertyChanged("SuggestionsVisible");
}
}
public ObservableCollection<String> SearchTopics = new ObservableCollection<String>();
}
The OnKeyUp() method is called by the MainWindow class ( haven't gotten as far as binding events to handlers specified in the ViewModel ), while but there's also a call to ResetSearechField from the MainWindow:
// Note: removing references to this event handler does not have any effect
// on the issue at hand... only including here for completeness
void window_Deactivated(object sender, EventArgs e)
{
SearchBoxVm.SuggestionsVisible = false;
SearchBoxVm.ResetSearchField(true);
}
I've spent quite a bit of time trying to debug this, and haven't seen any internal state changes that might account for this. The NotifyPropertyChanged event is otherwise behaving as it did before, and the stack trace window isn't showing any exceptions having been encountered.
Setting the binding mode on the IsDropdownOpen property to 'TwoWay' in the XAML hasn't had any effect either. Lastly, wrapping the assignment to SuggestionsVisible in a Dispatcher call on the main thread has had no effect on the issue either.
Any assistance would be appreciated.
#BrMcMullin, since you have stated that:
The desired effect is a search box with autocomplete suggestions.
may I ask, why do you choose to use standard ComboBox instead of specialized AutoCompleteBox that is available in the WPF Toolkit - February 2010 Release and seems like was especially designed for your case?
You may have noticed that first link points to documentation for its Silverlight predecessor, but don't worry - WPF Toolkit library include fully functional official WPF port of AutoCompleteBox from Silverlight. There is more info about this "event": AutoCompleteBox: Now with 100% more WPF.
With that control your auto complete popup could looks as simple as:
or as complex as:
So, if you will not manage to solve your issue with ComboBox's popup visibility, feel free to give a try to AutoCompleteBox. With it you could even leverage dynamic sorting of your suggestions if needed (just use answer from #adabyron).
I will explain what I mean.
Let's say I wrapped TextBox into UserControl and expose property Id
In order to bind to this property, it has to be Dependency property. Fine, here we go(notice stupid dance with OnIdChanged calls property setter so we get INotifyPropertyChanged working):
public static readonly DependencyProperty IdProperty =
DependencyProperty.RegisterAttached("Id", typeof(string), typeof(MyTextBox), new PropertyMetadata(OnIdChanged));
public string Id
{
get
{
return (string)GetValue(IdProperty);
}
set
{
this.SetValue(IdProperty, value);
this.OnPropertyChanged("Id");
}
}
private static void OnIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Lookup).Id = e.NewValue as string;
}
So, this seems like all I need. I create another User control. Drop MyTextBox on it:
<Lookup:MyTextBox Id="{Binding Source={StaticResource DataContextProxy}, Path=DataSource.CurrentItem.DeviceId, Mode=TwoWay, NotifyOnValidationError=True}"/>
As you see - I had to use DataContextProxy. To be honest, it's little bit of magic for me, I did it once and tried it now when regular way wasn't binding. How should I code my user control so I can bind to it like so?
<Lookup:MyTextBox Id="{Binding Path=CurrentItem.DeviceId, Mode=TwoWay, NotifyOnValidationError=True}"/>
This is how I can bind TextBox next to my custom one and it works as expected. What is the secret?
EDIT
Below is 2 screenshots. First one shows how what I get as a source when I bind to Lookup control (custom UserControl) - points to SELF
Second one - next field in my XAML - is regular textbox, binds to same CurrentItem but it sources from my ViewModel
EDIT 2
I figured why DataContext was pointing to UserControl itself. I figured why but do not understand why..
In my UserControl (Lookup) code behind after initializiation I set this.DataContext = this so inside control it binds to internal properties. Somehow it propogated to parent ViewModel. After I changed this code to LayoutRoot.DataContext = this - issue resolved. But I don't understand why it behaves like this and I still can't get good property routing through..
I covered this issue in a blog post which I wrote some time ago. If you set the DataContext of the UserControl to itself, you can no longer place it within another UserControl or Window and expect it to inherit the DataContext of its parent. This means that you cannot just sit it in your view and specify bindings to your view model. The reasons for this is that you have blocked inheritence of your ViewModel DataContext. Any properties exposed by your UserControl will have their binding Source set to the UserControl.
The solution is to set DataContext of some element within the UserControl to the UserControl itself. Most typically you would set the immediate child of the UserControl.