Change binded object property on button click in WPF - wpf

I have binded an object to a WPF control. How can I toggle the object property "IsEditMode" on click of the edit button using only xaml and no code behind? Here is sample code of xaml -
<Label Style="{StaticResource TitleLabel}"
Content="{Binding Path=GroupTitle}"
Visibility="{Binding Path=IsEditMode, Converter={StaticResource boolToVis}}"
HorizontalAlignment="Left" />
<Button Content="Edit" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<!--Toggle the bindedobject.IsEditMode property of click of button-->
</EventTrigger>
</Button.Triggers>
</Button>

using only xaml and no code behind
I don't think it's possible with no C# (or VB) code at all, but you can do it with no code-behind, using the MVVM pattern. So you will have C# code, just not in the code-behind...
If you go this way, you need to expose a command from your ViewModel:
private DelegateCommand _enterEditModeCommand;
public ICommand EnterEditModeCommand
{
get
{
if (_enterEditModeCommand== null)
{
_enterEditModeCommand= new DelegateCommand(EnterEditMode);
}
return _enterEditModeCommand;
}
}
private void EnterEditMode()
{
IsEditMode = true;
}
And bind your button to that command:
<Button Content="Edit" Command="{Binding EnterEditModeCommand}"
HorizontalAlignment="Right" VerticalAlignment="Center">

There's a control for that already in the framework:
<Label Style="{StaticResource TitleLabel}"
Content="{Binding Path=GroupTitle}"
Visibility="{Binding Path=IsEditMode, Converter={StaticResource boolToVis}}"
HorizontalAlignment="Left" />
<ToggleButton Content="Edit" HorizontalAlignment="Right" VerticalAlignment="Center"
IsChecked="{Binding IsEditMode}"/>

Related

WPF Tooltip binding and open programatically does not work

I have a Custom tooltip icon which also has its content bound to Datacontext. My need was to open Tooltop on mouse hover as well as on click event. So I used following code
<Image Source="/MainUI;component\Images\home\tooltip_info.png"
Width="24" Height="24" Stretch="Fill" HorizontalAlignment="Right"
Name="ImageToolTip"
Margin="0,0,0,0" MouseUp="ImageToolTip_MouseUp" MouseLeave="ImageToolTip_MouseLeave">
<Image.ToolTip>
<ToolTip BorderBrush="Transparent" Background="Transparent" HorizontalOffset="-142">
<TextBlock TextWrapping="WrapWithOverflow"
Style="{StaticResource ExcalFont-MSFD300}"
FontSize="14" Text="{Binding Tips}"
Width="300" Padding="15,15,15,15">
<TextBlock.Background>
<ImageBrush ImageSource="Images\home\popupbox.png" />
</TextBlock.Background>
</TextBlock>
</ToolTip>
</Image.ToolTip>
</Image>
Code Behind:
private void ImageToolTip_MouseUp(object sender, MouseButtonEventArgs e)
{
((ToolTip)((FrameworkElement)sender).ToolTip).IsOpen = true;
}
private void ImageToolTip_MouseLeave(object sender, MouseEventArgs e)
{
((ToolTip)((FrameworkElement)sender).ToolTip).IsOpen = false;
}
Now the issue is on mouse up It opens Tooltip but it does not bind the text.
This is working fine if I am using static text instead of Binding. What am I doing wrong?
In case I mouse hover on icon then it works fine and shows the content too. Thereafter everytime mouse click also shows the tooltip content. Not sure why mouse click do not work initially. –
ToolTip is not part of the VisualTree.
A problem that is similar to the problem you have is described here:
RelativeSource binding from a ToolTip or ContextMenu
One option to solve your problem would be something like this:
<Image Source="/MainUI;component\Images\home\tooltip_info.png"
Width="24" Height="24" Stretch="Fill" HorizontalAlignment="Left"
Name="ImageToolTip"
Margin="0,0,0,0" MouseUp="ImageToolTip_MouseUp" MouseLeave="ImageToolTip_MouseLeave" Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=Self}}">
<Image.ToolTip>
<ToolTip BorderBrush="Transparent" Background="Transparent" HorizontalOffset="-142" >
<TextBlock TextWrapping="WrapWithOverflow"
FontSize="14" Text="{Binding PlacementTarget.Tag.Tips,
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToolTip}}"
Width="300" Padding="15,15,15,15">
<TextBlock.Background>
<ImageBrush ImageSource="Images\home\popupbox.png" />
</TextBlock.Background>
</TextBlock>
</ToolTip>
</Image.ToolTip>
</Image>

MVVM : Binding Commands with Collection to Listbox in WPF

i have a list box in which there are different controls like button , text box etc in its item template, i have a collection which i bind with listbox, it works fine , but now i want to move my code to MVVM , and i write some commands in my View Model for clicks events of buttons , how can i bind my collection + my commands to list box ??? because commands are not in the collection, this is the Data Template for my list Box
<DataTemplate x:Key="listItemTemplate">
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Name="commentsPanel" LastChildFill="False" MinWidth="350">
<TextBlock Name="txtUserName" IsEnabled="False" Text="{Binding UserName}"
Width="Auto" DockPanel.Dock="Left" Foreground="GhostWhite" Margin="0,6,0,0"></TextBlock>
<TextBlock Name="txtDate" IsEnabled="False" Text="{Binding CreateDt}"
Width="Auto" DockPanel.Dock="Left" Foreground="Green" Margin="4,6,0,0"></TextBlock>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Width="{Binding EditPanelWidth}" x:Name="EditDeletePanel" Visibility="{Binding ButtonVisibilityText }">
<Button Name="btnEdit" Content="Edit" Width="Auto" DockPanel.Dock="Right" Height="20"
Click="btnEdit_Click_1" Margin="4,4,0,4" Foreground="GhostWhite" VerticalContentAlignment="Top" Visibility="{Binding ButtonVisibilityText}"></Button>
<Button Name="btnDelete" Content="Delete" Width="Auto" Height="20" VerticalContentAlignment="Top" DockPanel.Dock="Right" Visibility="{Binding ButtonVisibilityText}"
Click="btnDelete_Click_1" Margin="4"></Button>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" x:Name="SaveCancelPanel" Visibility="{Binding CancelSaveEnableText}">
<Button Name="btnSave" Content="Save" Width="Auto" Height="20" DockPanel.Dock="Right"
Click="btnSave_Click_1" Margin="4"></Button>
<Button Name="btnCancel" Content="Cancel" Height="20" Width="Auto" DockPanel.Dock="Right"
Click="btnCancel_Click_1" Margin="4"></Button>
</StackPanel>
</DockPanel>
<dxe:TextEdit ShowBorder="False" Grid.Row="1" Name="txtComment" Width="Auto" Foreground="Red"
TextWrapping="WrapWithOverflow" EditValue="{Binding Note}" IsEnabled="{Binding IsCommentTextEnable}">
</dxe:TextEdit>
<dxe:TextEdit Text=".............." Grid.Row="2" ShowBorder="False" IsEnabled="False">
</dxe:TextEdit>
</Grid>
</DataTemplate>
and here is the collection + my commands which i want to bind to buttons ,
public ICommand CancelCommand
{
get { return _cancelCommand ?? (_cancelCommand = new CommandHandler(Cancel)); }
set { _cancelCommand = value; }
}
public TList<ProgramNote> NotesCollection
{
get { return _notes; }
set
{
_notes = value;
RaisePropertyChanged("NotesCollection");
}
}
I know i can use this code to bind my commands with button
<Button Command={Binding CancelCommand}
but this command is not present in the collection , i am new in MVVM , kindly help , may be i am missing some little thing to bind my commands , but i am confused that how to add commands in my collection , so that i can get them in my view
You can bind the commands to your data template buttons etc by finding the appropriate viewmodel
example
<DataTemplate x:Key="listItemTemplate">
<Button Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=LixtBox}}"
CommandParameter="{Binding}">
</DataTemplate>
in example we'll find the datacontext of LixtBox which I assume to be your viewmodel then will bind to the command and pass the current object as the command parameter to perform actions on.
you'll then receive the item as parameter in the implementation of your command
public void Execute(object parameter)
{
ProgramNote note = parameter as ProgramNote;
//your logic here, eg cancelling download etc.
}
Thanx to all of you specially thanx to #Sheridan and #PushPraj, I am able to do it now , here is the code of data template in which i have a button
<Button Name="btnCancel" Content="Cancel" Height="20" Width="Auto" DockPanel.Dock="Right"
Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dxe:ListBoxEdit}}"
CommandParameter="{Binding}" Margin="4"></Button>
and this is the code of ListBox
<dxe:ListBoxEdit Name="listComments" Grid.Row="1" ItemTemplate="{StaticResource listItemTemplate}"
ItemsSource="{Binding NotesCollection}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Visible" >
</dxe:ListBoxEdit>
and lastly this is my back end code
listComments.DataContext = viewModel;
It's always difficult to answer new user's questions because they always leave out important information from their questions. However, judging by your question text, it sounds to me like you have set your collection property as the DataContext of your Window. If you want to data bind to your commands instead, then you'll need to change the DataContext to the object that contains both the collection and the commands... an instance of your view model:
DataContext = new YouViewModel();
Now that the DataContext is set to an instance of your view model, you can data bind to its properties as you showed us:
<Button Command="{Binding CancelCommand}" />
...
<ListBox ItemsSource="{Binding NotesCollection}" />
Ahhh, sorry I misunderstood - so your Button is inside the ListBox. In that case, you could try something like this:
<Button Command="{Binding DataContext.CancelCommand,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
This should reach out of the scope of the collection, looking in the DataContext of a Window, so if you have set the instance of your view model to the Window.DataContext, this should work.

Button's IsEnabled property depends on Command property?

I am surprised by the following behavior of Button.
I have created a usercontrol for button + image.
<UserControl x:Name="root">
<Button Command="{Binding ClickCommand, ElementName=root}" Click="Button_Click">
<StackPanel>
<Image Source="{Binding ImageSource, ElementName=root, Mode=OneWay}"
Stretch="None" />
<TextBlock Padding="4,0"
Text="{Binding Caption, ElementName=root, Mode=OneWay}" />
</StackPanel>
</Button>
</UserControl>
The ClickEvent and ClickCommandProperty are in code-behind as RoutedEvent and DependencyProperty.
It works as expected, but I do not understand why the UserControl cannot be enabled when I do not set the ClickCommand in xaml at least to an empty string.
<c:ImageButton Caption="Refresh" Click="refresh_Click" x:Name=btnRefresh
ClickCommand="" ImageSource="{StaticResource refresh}" />
So without ClickCommand="" the code btnRefresh.IsEnabled = true; has no effect.
Also: the IsEnabledChanged event is invoked by .IsEnabled = true; and not again to set it to false? Mysteries.

Click event is not raised for Button in popup on datagrid

I'am quite new with WPF and still trying to figure it out in all its details ;)
I have a strange issue with the button not raising its click event. I have a UserControl called PopupButton which is basically a button with a popup showed on click. The content of the popup is ContentPresenter and is binded to the dependency property called PopupContentHolder on the PopupButton.
Then on the main form I have a DataGrid with DataGridTemplateColumn where in the <DataGridTemplateColumn.CellTemplate> <DataTemplate> I placed my PopupButton. Then I assign a list box with an ItemTemplate as the content of the popup for my PopupButton.
In simple this looks as follows
<DataGrid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
ColumnHeaderHeight="40"
FontSize="11"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Pocos}"
RowHeight="30"
SnapsToDevicePixels="True"
VerticalScrollBarVisibility="Disabled">
<DataGrid.Columns>
<DataGridTemplateColumn MinWidth="70" Header="NAme">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<controls:PopupButton DoPopupOnMainButton="True"
Foreground="{StaticResource HeaderLinkActiveColor}"
MarkButtonOnPopup="True"
PopupBackground="{StaticResource PopupBackground}"
PopupPadding="5"
ShowDownArrow="False"
Text="Some test button">
<controls:PopupButton.PopupContentHolder>
<StackPanel>
<ListBox Background="Transparent" ItemsSource="{Binding Tenders}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Margin="0,2,0,0"
Click="Button_Click"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}},
Path=DataContext.SaveCommand}"
Content="{Binding .}"
FontSize="11" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</controls:PopupButton.PopupContentHolder>
</controls:PopupButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
Now the problem is with the button inside the ListBox in PopupButton, Neither Click event nor Command is raised. Moreover when I put the PopupButton outside the DataGrid then everythinkg works perfect. Also when I put above list box directly in grid cell then it works.
Maybe its something wrong with my PopupButtonControl.
<UserControl x:Class="WpfApplication1.PopupButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="userControlRoot"
Width="Auto"
Height="Auto">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<Storyboard x:Key="PopupCloseStoryBoard">
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="popup" Storyboard.TargetProperty="IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.1" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="PopupOpenStoryBoard">
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="popup" Storyboard.TargetProperty="IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<Grid>
<Border x:Name="brdButtonBack"
Background="Transparent"
CornerRadius="3,3,0,0"
Padding="{Binding Padding,
ElementName=userControlRoot}" />
<Button x:Name="btnMain"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Click="btn_Click"
Command="{Binding MainCommand,
ElementName=userControlRoot}"
Content="{Binding Text,
ElementName=userControlRoot}"
IsEnabled="{Binding IsEnabled,
ElementName=userControlRoot}" />
<Popup x:Name="popup"
MinWidth="{Binding ActualWidth,
ElementName=userControlRoot}"
AllowsTransparency="True"
Placement="Bottom"
PlacementTarget="{Binding ElementName=brdButtonBack}"
PopupAnimation="Slide"
StaysOpen="False">
<Border Background="{Binding PopupBackground,
ElementName=userControlRoot}"
BorderBrush="White"
CornerRadius="0,3,3,3"
Padding="{Binding PopupPadding,
ElementName=userControlRoot}">
<ContentPresenter Content="{Binding PopupContentHolder, ElementName=userControlRoot}" />
</Border>
</Popup>
</Grid>
I have simplified the control but the problem reproduces with that version too. The code behind for the control mostly contains dependency properties, the only logic is shown below
public partial class PopupButton : UserControl
{
//... dependency properties
public PopupButton()
{
InitializeComponent();
}
private void btn_Click(object sender, RoutedEventArgs e)
{
Storyboard s = (Storyboard)TryFindResource("PopupOpenStoryBoard");
s.Begin();
}
private void popup_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//trick to close popup when the button inside is clicked
//this must be done with storyboard and delay
//since popup can not be closed before the event reaches clicked
//button
if (e.Source is Button)
{
Storyboard s = (Storyboard)TryFindResource("PopupCloseStoryBoard");
s.Begin();
}
}
}
Has anyone any clue what can be wrong with it?
Thanks.
I did finally solve this by rewriting PopupButton as CustomControl. There, instead of firing StoryBoard to close the popup, when the contained button is clicked I used following code
mainPopup.AddHandler(Button.ClickEvent, new RoutedEventHandler(mainPopup_ButtonClick), true);
Adding a button click handler for main popup, which will be called on the popup even if the event is already handled by the source button.
void mainPopup_ButtonClick(object sender, RoutedEventArgs e)
{
MainPopup.IsOpen = false;
}
More info about handling already handled routed events here
Maybe it is just a typo because there is a btn_Click and Button_Click event handler in your code.
Would you mind providing the code of the PopupContentHolder or maybe the whole PopupButton class?

Any way to reuse Bindings in WPF?

I'm getting to the point in a WPF application where all of the bindings on my controls are getting quite repetitive and also a little too verbose. Also if I want to change this binding I would have to change it in various places instead of just one.
Is there any way to write the source part of the binding once such as in a resource and then reuse it by referencing it with a more compact syntax. I've looked around for such capabilities but I haven't found it.
What I'm doing now
<StackPanel>
<ToggleButton x:Name="someToggleButton" />
<Button Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
<Grid Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBox Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
<CheckBox Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
What I want to be able to do (Pseudocode)
<StackPanel>
<StackPanel.Resources>
<Variable x:Name="someToggleButtonIsChecked"
Type="{x:Type Visibility}"
Value="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel.Resources>
<ToggleButton x:Name="someToggleButton" />
<Button Visibility="{VariableBinding someToggleButtonIsChecked}" />
<Grid Visibility="{VariableBinding someToggleButtonIsChecked}" />
<TextBox Visibility="{VariableBinding someToggleButtonIsChecked}" />
<CheckBox Visibility="{VariableBinding someToggleButtonIsChecked}" />
</StackPanel>
Is there any similar type of similar feature or technique that will allow me to declare the binding source once and then reuse it?
You can just bind someToggleButton's IsChecked property to a property on your viewmodel (the DataContext) and use that. It would look something like this:
<StackPanel>
<ToggleButton x:Name="someToggleButton" IsChecked="{Binding ToggleVisibility, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<Button Visibility="{Binding ToggleVisibility}" />
<Grid Visibility="{Binding ToggleVisibility}" />
<TextBox Visibility="{Binding ToggleVisibility}" />
<CheckBox Visibility="{Binding ToggleVisibility}" />
</StackPanel>
This would require that your Window's DataContext has a property called ToggleVisibility of type Visibility.
EDIT:
To eleborate further, your viewmodel could look like this:
public class SomeViewModel : INotifyPropertyChanged
{
private Visibility toggleVisibility;
public SomeViewModel()
{
this.toggleVisibility = Visibility.Visible;
}
public Visibility ToggleVisibility
{
get
{
return this.toggleVisibility;
}
set
{
this.toggleVisibility = value;
RaisePropertyChanged("ToggleVisibility");
}
}
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
And you would then set an instance of it as the DataContext on the Window or even just on the StackPanel
Is there any way to write the source part of the binding once such as in a resource and then reuse it by referencing it with a more compact syntax.
Perhaps you can do that with PyBinding. I don't know the extent of its capabilities, but I use it all the time ot avoid type converters. Here is an example I use a lot.
Visibility="{p:PyBinding BooleanToVisibility(IsNotNull($[.InstanceName]))}"
BooleanToVisibility is a function I wrote in IronPython.
$[.InstanceName] binds to the InstanceName property of the current data-bound item.
EDIT: You can also use this to bind one UI's property to another's. Here is some info from the help file.
$[NameTextBlock.Text] - The text property of the element with x:Name equal to "NameTextBlock"
$[NameTextBlock] - An actual TextBlock instance, rather than one of its properties
$[{Self}] - Bind to your self. Equivalent to {Binding RelativeSource={RelativeSource Self}}
$[{Self}.Text] - The Text property off your self. Equivalent to {Binding Path=Text, RelativeSource={RelativeSource Self}}
http://pybinding.codeplex.com/
Untested Theory
<StackPanel>
<ToggleButton x:Name="someToggleButton" />
<Button Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}" />
<Grid Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/>
<TextBox Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/>
<CheckBox Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/>
</StackPanel>
Second Attempt
<StackPanel>
<ToggleButton x:Name="someToggleButton" />
<Button Name="myButton" Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}" />
<Grid Visibility="{p:PyBinding $[myButton.Visibility]}"/>
<TextBox Visibility="{p:PyBinding $[myButton.Visibility]}"/>
<CheckBox Visibility="{p:PyBinding $[myButton.Visibility]}"/>
</StackPanel>
Just looking at the original code, you could group the necessary elements into their own container and then manage the container Visibility:
<StackPanel>
<ToggleButton x:Name="someToggleButton" />
<StackPanel Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">
<Button />
<Grid />
<TextBox />
<CheckBox />
</StackPanel>
</StackPanel>
Actually, today I would do this with the VSM - have a state with the elements Visible and a state with them not Visible, then use two GoToState Behaviors on the Toggle button to set the state based on the button's toggle state.

Resources