I have 2 WPF forms.
In form1, I have 2 buttons(add,edit) and in form2, 3 fields. When I click on 1 of 2 buttons open form2. How to make, when I click on button Add in second form all fields are 'IsEnabled="True"', but when I click Edit button, 1 field to be IsEnabled="False" in second form!?
10x
First, create a public bool property to bind to:
private bool canEdit = false;
public bool CanEdit
{
get { return canEdit; }
set { canEdit = value; NotifyPropertyChanged("CanEdit"); }
}
Then bind it to the IsEnabled properties of the TextBox controls:
<TextBox IsEnabled="{Binding CanEdit, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type YourXmlNamespace:YourParentForm}}}" />
Then just set it to true in either your Click handler, or your Command handler to make the TextBox controls enabled:
CanEdit = true; // Makes TextBox.IsEnabled = true
Notes:
You may prefer to add another bool property for your one control that should be enabled when the edit button is clicked. Also, you may prefer to use the IsReadOnly property instead of IsEnabled.
UPDATE >>>
I'm assuming that either your second form is launched from your first form, or they are both launched from somewhere else... in this case, you can add the property to the parent form and bind to it from the child form(s) with the RelativeSource binding that I have shown in the example.
You can bind
Related
I'm using WPF, and I have a form, on which there are many textboxes, buttons and whatnot.
For a specific reason I need that whole form to be disabled, which I managed with the code below -
public WPForm() //constructor
{
this.IsEnabled = false;
}
But, I have one button that sends me back to another form, which I need to be enabled (clickable), and what I have tried is simply setting it like this -
public WPForm() //constructor
{
this.IsEnabled = false;
btnBack.IsEnabled = true;
}
And this does basically nothing, so my question is, what else do I need to do in order to make it enabled?
Just dont set the whole Form to disabled.
Try to put a Grid or Stackpanel around the Controls that need to be disabled and disable them only. Maybe like so:
<Grid x:Name="myGrid">
//Your Controls
<Grid/>
<Button />
public WinForm() //constructor
{
myGrid.IsEnabled = false;
}
I have a WPF application with multiple tabs. Under each tab, a user can change some settings (using CheckBoxes, TextBoxes, etc) and then the user must click the "Update" button in order to save those settings. Everything works fine but one of requirements is to alert the user if he tries to switch to other tab without clicking the "Update" button.
So I'm trying to use the
TabItem_LostFocus
event handler to achieve it but this event is triggered every time I click on something within the tab. I guess I can patch this issue by placing
e.Handled = true
for every control I have but this doesn't sound like an elegant solution (especially when I don't have click event handlers for everything under my tabs). Is there some other way to determine when you are switching away from the current tab?
Thank you
To achieve your requirement, you just need to data bind to the TabControl.SelectedIndex or the TabControl.SelectedItem properties:
<TabControl ItemsSource="{Binding TabItemCollection}"
SelectedItem="{Binding SelectedTabItem}" />
Then in your view model or code behind:
private YourDataType selectedItem;
public YourDataType SelectedItem
{
get { return selectedItem; }
set
{
// selectedItem represents the previous TabItem
// value represents the new TabItem
selectedItem = value;
}
}
You can bind to the IsSelected property of each TabItem.. and then do your checking inside the setter
<TabControl>
<TabItem IsSelected="{Binding TabItem1IsSelected}"/>
</TabControl>
Property:
public bool TabItem1IsSelected
{
get { return _tabItem1IsSelected; }
set
{
if (_tabItem1IsSelected)
{
if (!value)
{
// Check to see if user has updated
if (!userUpdated)
{
value = true;
}
}
}
_tabItem1IsSelected = value;
RaisePropertyChanged();
}
}
I have a problem with data binding. I am using MVVM Light.
When I set a breakpoint on the setter of one of the bools in my view model and select the corresponding radio button, the debugger halts and all looks fine.
Then when I continue, select the other radio button and select the first radio button again, the debugger is not halting. What is going wrong?
I have two two radiobuttons:
<RadioButton Margin="5" Grid.Row="1" Content="Browser cookies" GroupName="loginmethod" IsChecked="{Binding IsBrowserCookiesChecked}"/>
<RadioButton Margin="5" Grid.Row="2" Content="Username + password" GroupName="loginmethod" IsChecked="{Binding IsUsernamePasswordChecked}"/>
I have bounded them to two separate bools in my view model which looks as follows:
public const string IsUsernamePasswordCheckedPropertyName = "IsUsernamePasswordChecked";
private bool _isUsernamePasswordChecked = false;
public bool IsUsernamePasswordChecked
{
get
{
return _isUsernamePasswordChecked;
}
set
{
if (_isUsernamePasswordChecked == value)
{
return;
}
RaisePropertyChanging(IsUsernamePasswordCheckedPropertyName);
_isUsernamePasswordChecked = value;
RaisePropertyChanged(IsUsernamePasswordCheckedPropertyName);
}
}
public const string IsBrowserCookiesCheckedPropertyName = "IsBrowserCookiesChecked";
private bool _isBrowserCookiesChecked = true;
public bool IsBrowserCookiesChecked
{
get
{
return _isBrowserCookiesChecked;
}
set
{
if (_isBrowserCookiesChecked == value)
{
return;
}
RaisePropertyChanging(IsBrowserCookiesCheckedPropertyName);
_isBrowserCookiesChecked = value;
RaisePropertyChanged(IsBrowserCookiesCheckedPropertyName);
}
}
I find that when you're doing databinding on radio buttons it's a lot easier to give each button a different GroupName so they don't affect each other, then handle the setting and unsetting of each radio button choice via binding only. For example you could expose just one boolean on your viewmodel, have one radio button bind to that bool, then have the other bind to the same one, but with a inverse boolean converter. Or make the property an enum, then assign each radio button to that property, but with an enum to boolean converter. Then you have the added benefit of fewer properties to worry about on your viewmodel.
When you give radio buttons the same GroupName WPF will try to mess with the other properties every time you change one and you can get some ugly loops and weird behavior.
I have a UserControl say Stock and it has a Button called Display
<Button Command="{Binding DisplayCommand}" CommandParameter="StockGroups">Display</Button>
Now when i Click this button it should add an another UserControl named Display to the Canvas which is in HomeWindow and should pass the CommandParameter to the Display userControl.
private DelegateCommand<string> _displayCommand;
public virtual void DisplayExecuted(string param){}
public ICommand DisplayCommand
{
get
{
if (_displayCommand == null)
_displayCommand = new DelegateCommand<string>(new Action<string>(DisplayExecuted));
return _displayCommand;
}
}
An alternative method which is more MVVM-ish would be to have a boolean property named ShouldDisplayControl, which is then bound to the control's Visibility property (using the [BooleanToVisibilityConverter]) 1), while passing the CommandParameter as a second property, maybe ControlParameter, which the control is also bound to.
This is not an operation that should involve the ViewModel, since it does not manipulate any model data.
Instead of a ViewModel command, consider merely handling the button's OnClick in the code-behind of the xaml.
In your HomeWindow.xaml.cs file:
protected override void Display_OnClick(object sender, EventArgs e) {
var buttonName = ((Button)sender).Name; // gets the name of the button that triggered this event
var displayControl = new DisplayControl(); // your user control
displayControl.param = buttonName; // set the desired property on your display control to the name of the button that was clicked
((Canvas)Content).Children.Add(displayControl); // 'Content' is your Canvas element
}
And in your HomeWindow.xaml file:
<Button x:Name="StockGroups" Click="Display_OnClick" Text="Display" />
That should get you what you want, without needing to create and invoke a command in the viewmodel. The name of the clicked button will be set to the specified property in your userControl, and an instance of the control will be created inside the Canvas.
I have a requirement to focus on a specific textbox when a new view is loaded.
The solution was to add this line of code to the OnLoaded event for the view:
Dispatcher.BeginInvoke(() => { NameTextBox.Focus(); });
So this worked for one view, but not another. I spent some time debugging the problem and realized that the new view I was working on had a BusyIndicator that takes focus away from all controls since the BusyIndicator being set to true and false was occuring after the OnLoaded event.
So the solution is to call focus to the NameTextBox after my BusyIndicator has been set to false. My idea was to create a reusable BusyIndicator control that handles this extra work. However, I am having trouble doing this in MVVM.
I started by making a simple extension of the toolkit:BusyIndicator:
public class EnhancedBusyIndicator : BusyIndicator
{
public UserControl ControlToFocusOn { get; set; }
private bool _remoteFocusIsEnabled = false;
public bool RemoteFocusIsEnabled
{
get
{
return _remoteFocusIsEnabled;
}
set
{
if (value == true)
EnableRemoteFocus();
}
}
private void EnableRemoteFocus()
{
if (ControlToFocusOn.IsNotNull())
Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); });
else
throw new InvalidOperationException("ControlToFocusOn has not been set.");
}
I added the control to my XAML file with no problem:
<my:EnhancedBusyIndicator
ControlToFocusOn="{Binding ElementName=NameTextBox}"
RemoteFocusIsEnabled="{Binding IsRemoteFocusEnabled}"
IsBusy="{Binding IsDetailsBusyIndicatorActive}"
...
>
...
<my:myTextBox (this extends TextBox)
x:Name="NameTextBox"
...
/>
...
</my:EnhancedBusyIndicator>
So the idea is when IsRemoteFocusEnabled is set to true in my ViewModel (which I do after I've set IsBusy to false in the ViewModel), focus will be set to NameTextBox. And if it works, others could use the EnhancedBusyIndicator and just bind to a different control and enable the focus appropriately in their own ViewModels, assuming their views have an intial BusyIndicator active.
However, I get this exception when the view is loaded:
Set property 'foo.Controls.EnhancedBusyIndicator.ControlToFocusOn' threw an exception. [Line: 45 Position: 26]
Will this solution I am attempting work? If so, what is wrong with what I have thus far (cannot set the ControlToFocusOn property)?
Update 1
I installed Visual Studio 10 Tools for Silverlight 5 and got a better error message when navigating to the new view. Now I gete this error message:
"System.ArgumentException: Object of type System.Windows.Data.Binding cannot be converted to type System.Windows.Controls.UserControl"
Also, I think I need to change the DataContext for this control. In the code-behind constructor, DataContext is set to my ViewModel. I tried adding a DataContext property to the EnhancedBusyIndicator, but that did not work:
<my:EnhancedBusyIndicator
DataContext="{Binding RelativeSource={RelativeSource Self}}"
ControlToFocusOn="{Binding ElementName=NameTextBox}"
RemoteFocusIsEnabled="{Binding IsRemoteFocusEnabled}"
IsBusy="{Binding IsDetailsBusyIndicatorActive}"
...
>
Update 2
I need to change UserControl to Control since I will be wanting to set focus to TextBox objects (which implement Control). However, this does not solve the issue.
#Matt, not sure
DataContext="{Binding RelativeSource={RelativeSource Self}}"
will work in Silverlight 5, have you tried binding it as a static resource?
Without a BusyIndicator present in the view, the common solution to solve the focus problem is to add the code
Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); });
to the Loaded event of the view. This actually works even with the BusyIndicator present; however, the BusyIndicator immediately takes focus away from the rest of the Silverlight controls. The solution is to invoke the Focus() method of the control after the BusyIndicator is not busy.
I was able to solve it by making a control like this:
public class EnhancedBusyIndicator : BusyIndicator
{
public EnhancedBusyIndicator()
{
Loaded += new RoutedEventHandler(EnhancedBusyIndicator_Loaded);
}
void EnhancedBusyIndicator_Loaded(object sender, RoutedEventArgs e)
{
AllowedToFocus = true;
}
private readonly DependencyProperty AllowedToFocusProperty = DependencyProperty.Register("AllowedToFocus", typeof(bool), typeof(EnhancedBusyIndicator), new PropertyMetadata(true));
public bool AllowedToFocus
{
get { return (bool)GetValue(AllowedToFocusProperty); }
set { SetValue(AllowedToFocusProperty, value); }
}
public readonly DependencyProperty ControlToFocusOnProperty = DependencyProperty.Register("ControlToFocusOn", typeof(Control), typeof(EnhancedBusyIndicator), null);
public Control ControlToFocusOn
{
get { return (Control)GetValue(ControlToFocusOnProperty); }
set { SetValue(ControlToFocusOnProperty, value); }
}
protected override void OnIsBusyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsBusyChanged(e);
if (AllowedToFocus && !IsBusy)
{
Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); });
AllowedToFocus = false;
}
}
}
To use it, replace the BusyIndicator tags in your xaml with the new EnhancedBusyIndicator and add the appropriate namespace.
Add a new property, ControlToFocusOn inside the element, and bind it to an existing element in the view that you want focus to be on after the EnhancedBusyIndicator disappears:
<my:EnhancedBusyIndicator
ControlToFocusOn="{Binding ElementName=NameTextBox}"
...
>
...
</my:EnhancedBusyIndicator>
In this case, I focused to a textbox called NameTextBox.
That's it. This control will get focus every time we navigate to the page. While we are on the page, if the EnhancedBusyIndicator becomes busy and not busy agiain, focus will not go to the control; this only happens on initial load.
If you want to allow the EnhancedBusyIndicator to focus to the ControlToFocusOn another time, add another property, AllowedToFocus:
<my:EnhancedBusyIndicator
ControlToFocusOn="{Binding ElementName=NameTextBox}"
AllowedToFocus="{Binding IsAllowedToFocus}"
...
>
...
</my:EnhancedBusyIndicator>
When AllowedToFocus is set to true, the next time EnhancedBusyIndicator switches from busy to not busy, focus will go to ControlToFocusOn.
AllowedToFocus can also be set to false when loading the view, to prevent focus from going to a control. If you bind AllowedToFocus to a ViewModel property, you may need to change the BindingMode. By default, it is OneTime.