Bind IsEnable property of button located inside data grid to variable - wpf

I want to bind IsEnable property of button to variable. Button is added inside data grid. Data Grid is located inside user control.
Below code is not working.
<DataGridTemplateColumn Header="Start" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="Start_Button"
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext}"
IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}},Path=IsStartButtonEnable}"
Command="{Binding StartCommand}">START
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Model contains-(ServerModel.cs)
private bool _isStartButtonEnable = false;
public bool IsStartButtonEnable
{
get
{
return _isStartButtonEnable;
}
set
{
_isStartButtonEnable = value;
this.OnPropertyChanged("IsStartButtonEnable");
}
}
View Model(ServerViewModel.cs) Contains -
public static ObservableCollection<ServerModel> DataGridServerList { get; set; }
UserControl (ServerUserControl.cs) contains -
this.DataContext = new ServerViewModel();
Requirements - UserControl contains 3 buttons-Create, Start and Stop. User clicks on Create Button. If Create commnd gets success, then Start button gets enabled.Then user will click on Start button. If start commnd gets executed successfully, then Stop button gets enabled and so on.

<DataGridTemplateColumn Header="Start" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="Start_Button"
IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext.IsStartButtonEnable}"
Command="{Binding StartCommand}">START
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I am using the DataContext of the UserControl directly and not that of the DataGrid.
In your case, indeed, the DataContext of the Button will be set to each item in the DataGrid. In order to access the property on your UserControl you need this change. Further more, you don't need to set the DataContext again so i removed that line.
You have two possibilities in my opinion. Either set the flag in the ViewModel and will be accessible by all Buttons through the Relative Source syntax or if you have it in the Model, you will have a flag for each row in the DataGrid. In either case you have the response:
If it is in the Model, set the DataGrid ItemsSource to your ObservableCollection and use
IsEnabled = {Binding IsStartButtonEnable }
2.If it is in the ViewModel, use
IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext.IsStartButtonEnable}"
I don't see other option but if you want it available for all Buttons in the DataGrid you have to set it in the ViewModel.

Related

WPF MVVM Enable\Disable telerik:RadComboBox inside GridViewDataColumn from ViewModel

In WPF, I am trying to disable combo boxes inside a telerik:GridViewDataColumn based on the IsComboBoxEnabled property of my ViewModel. If I set IsEnabled=false from XAML, it works and the combo box is disabled. However, when I bind IsEnabled to a value on my viewmodel, it is always enabled even when the value is set to false.
<telerik:GridViewDataColumn>
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<telerik:RadComboBox SelectedValue="{Binding SomeSelectedValue}"
ItemsSource="{Binding SomeItemsSource}"
IsEnabled="{Binding IsComboBoxEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewDataColumn>
public bool IsComboBoxEnabled
{
get
{
return _isComboBoxEnabled;
}
set
{
_isComboBoxEnabled = value;
RaisePropertyChanged("IsComboBoxEnabled");
}
}
The issue was due to me setting the IsComboBoxEnabled property from the viewmodel of the datagrid instead of the viewmodel of the individual record.

Bind Command to a ComboBoxItem in WPF

I have a wpf project with a ComboBox. The items there inside are filled in dynamically. So it is bound to a model which contains a Label and a Command.
If the User selects an item in the Dropdown / ComboBox the Command should be executed. I tried it with a DataTemplate containing a TextBlock and a Hyperlink for the Command. But the Command will only execute if a select the Label (Hyperlink) and not if I click on the whole item.
<ComboBox ItemsSource="{Binding Path=States}" SelectedItem="{Binding CurrentState}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding Command}" TextDecorations="None" Foreground="Black">
<TextBlock Text="{Binding Path=Label}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
So the question is now, how can I bind my Command to a ComboBoxItem?
A ComboBoxItem has no Command property but you could execute the command from the setter of the CurrentState property:
private State _currentState;
public State CurrentState
{
get { return _currentState; }
set
{
_currentState = value;
if (_currentState != null)
{
_currentState.Command.Execute(null);
}
}
}
This property will be set whenever you select an item in the ComboBox. The other option would be to handle the SelectionChanged event in the view.

ICommand not working in Silverlight User control

I have a UserControlBase with a grid. The grid contains a column with an Action.
<sdk:DataGridTemplateColumn Header="Action">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<Button Style="{StaticResource DataGridButton}" Command="{Binding Source={StaticResource NewsViewModel}, Path=ModifyNewsCommand}" Content="Modify" />
</StackPanel>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
My problem is with my Command. It is throwing me Technical Error which is my first problem, I don't know how to make this application throw me the real error message.
In the code behind of my user control I registered the events:
protected void RegisterMessages()
{
Messenger.Default.Register<string>(this, "NewNewsBtn_Click", NewNewsBtn_Click);
Messenger.Default.Register<string>(this, "ModifyNewsBtn_Click", ModifyNewsBtn_Click);
}
And in my constructor:
public NewsWindow(int underlyingId)
{
InitializeComponent();
this.RegisterMessages();
viewModel = new NewsViewModel(underlyingId);
ucNewsPanel.DataContext = viewModel;
}
My view model (NewsViewModel)
public ICommand ModifyNewsCommand
{
get
{
return new RelayCommand<string>(e =>
{
Messenger.Default.Send(string.Empty, "ModifyNewsBtn_Click");
});
}
}
What's weird here is that my NewNewsBtn is working, while my ModifyNewsBtn is not.
This button is outside the grid so it might make a difference on why it is working.
<Button x:Name="NewNewsBtn" MaxHeight="50" MaxWidth="100" Command="{Binding Path=NewNewsCommand}" Content="Add New" />
Your DataGrid will be bound to some collection, having a row per each item. Now the item is the DataContext for a row. What you need to do is bind your "Modify"-button to the parent DataContext. If you are using silverlight5 you can use an AncestorBinding:
<Button
Content="Modify"
Command="{Binding
Path=DataContext.ModifyNewsCommand,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
Your syntax looks okay:
<Button Style="{StaticResource DataGridButton}" Content="Modify"
Command="{Binding Source={StaticResource NewsViewModel}, Path=ModifyNewsCommand}"/>
But you set your viewModel in code. Am I right that you create StaticResource in XAML? If it is, then just delete setting DataContext from code-behind cause the following row Command="{Binding Source={StaticResource NewsViewModel}, Path=ModifyNewsCommand}" will see in the another instance of your viewModel. (cause you've create two instances of NewsViewModel as a StaticResource and in code-behind)
Update:
I needed the DataContext in code behind as I am passing a parameter to my view model. Any way I can do that if I removed it from code behind?
Then you should delete StaticResource from Binding of Command:
<Button Style="{StaticResource DataGridButton}" Command="{Binding ModifyNewsCommand}"
Content="Modify"/>
Cause you are referencing to another instance of NewsViewModel.

WPF MVVM DataTemplate: Inject Template ViewModel with Data from parent Viewmodel

I have an ObservableCollection in my parent ViewModel which I want to display in the parent View. So I've defined a child View and a child ViewModel handling button clicks.
How do I get each item from the ObservableCollection into the according child ViewModel without loosing my already setup RelayCommands for button click handling?
In my parent View Code-Behind the only thing I do is setting the DataContext to the appropiate ViewModel:
DataContext = new ParentViewModel();
In my parent View XAML I defined a ListBox to display a DataTemplate of my child View:
<ListBox
ItemsSource="{Binding Path=Items}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ChildViewModel}">
<views:ChildView Width="auto" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now in my ChildView I got several TextBlocks displaying the Binding Data and Buttons which should execute a file in a path specified within the ObservableCollection:
<TextBlock
Text="{Binding Path=Title}" />
...
<Button
Content="Start exe"
Tag="{Binding Path=ExePath}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding Path=OnButtonClicked}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
The Child ViewModel holds RelayCommands to handle the Button click event:
private RelayCommand onButtonClicked;
public ICommand OnButtonClicked
{
get
{
return onButtonClicked ??
(onButtonClicked =
new RelayCommand(ObeyOnButtonClicked, CanObeyOnButtonClicked));
}
}
private void ObeyOnButtonClicked()
{
... //Path conversion
Process.Start(pathToExe);
}
private bool CanObeyOnButtonClicked()
{
return true;
}
Now, within my child View's Code-Behind, when I add
DataContext = new SampleItemViewModel();
to the Constructor, the Button click is handled but the TextBoxes are all empty.
When i remove this line, the TextBoxes are filled correctly, but the Button Clicks are not handled.
How do I get both features working?
EDIT:
ParentViewModel:
private ObservableCollection<Item> items;
public ObservableCollection<Item> Items
{
get { return items; }
set
{
items= value;
OnPropertyChanged("Items");
}
}
... //Filling the Collection in Constructor
ChildViewModel contains just the above mentioned button click handlers.
EDIT:
I tried several things now but I don't know how to bind the Command from the ChildView to my ChildViewModel WITHOUT setting the DataContext of my ChildView to my ChildViewModel
You can remove the Event Trigger, as Button has a Command property.
<TextBlock Text="{Binding Path=Title}" />
...
<Button
Content="Start exe"
Tag="{Binding Path=ExePath}"
Command="{Binding Path=OnButtonClicked}"
>
</Button>
And set the DataContext :
<ListBox
ItemsSource="{Binding Path=Items}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ChildViewModel}">
<views:ChildView Width="auto" DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

ToggleButton group: Ensuring one item is always selected in a ListBox

I am trying to duplicate the left/center/right alignment toolbar buttons in Word. When you click the "Left Alignment" button the Center and Right buttons uncheck. I am using a WPF ListBox with ToggleButtons.
The problem is the user can click the Left Alignment button twice. The second click causes the button to uncheck and sets the underlying value to null. I'd like the second click to do nothing.
Ideas? Force the ListBox to always have one selected item? Prevent the null in the view model (need to refresh the ToggleButton binding)?
<ListBox ItemsSource="{x:Static domain:FieldAlignment.All}" SelectedValue="{Binding Focused.FieldAlignment}">
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}">
<TextBlock Text="{Binding Description}" />
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
yeah, i would prefer the radiobutton too for this case, but if you want to use togglebutton then maybe you can bind the isenabled property to ischecked so it cannot be cliked when it's checked
create custom control from ToggleButton,
in *.xaml.cs file, declare and define control
public class ToggleButton2 : ToggleButton
{
public bool IsNotCheckable
{
get { return (bool)GetValue(IsNotCheckableProperty); }
set { SetValue(IsNotCheckableProperty, value); }
}
// Using a DependencyProperty as the backing store for IsNotCheckable. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsNotCheckableProperty =
DependencyProperty.Register("IsNotCheckable", typeof(bool), typeof(ToggleButton2), new FrameworkPropertyMetadata((object)false));
protected override void OnToggle()
{
if(!IsNotCheckable)
{
base.OnToggle();
}
}
}
in *.xaml, replace ToggleButton with my:ToggleButton2, then you can bind IsNotCheckable to IsChecked, just like below,
<my:ToggleButton2 IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" IsNotCheckable="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked, Mode=OneWay}">
Rather than implementing this as ToggleButtons, I would use RadioButtons with custom templates. It would probably save you a lot of headache.

Resources