override IsEnabled property for Custom Control DualButton to set Visibility - wpf

I am working on WPF and have little knowledge of XAML customly created controls.
I have a custom control name 'DualButton' as follows :
<Controls:DualButton x:Name="StandardConferenceCancelButton"
Width="90"
Height="25"
Margin="2"
LeftButtonCommand="{Binding StandardModeConnectCommand}"
RightButtonCommand="{Binding ConferenceCancelCommand}"
>
<AccessText HorizontalAlignment="Center" Text="{x:Static I18N:TelephonyRegionViewRes.Standard}" />
</Controls:DualButton>
its 2 dependancy properties 'LeftButtonCommand' and 'RightButtonCommand'binds two different ICommands.
I want to set Visibility of this button to CanExecute of LeftButtonCommand so that when LeftButtonCommandCanExecute() returns true, that time only button gets visible.
I took dependancyProperty 'IsEnabled' what further I need to do in this

I know it's late, but might help others...
In your DataContext, create a property like IsVisible that returns
StandardModeConnectCommand.CanExecute()
In your window or user control, add the resource
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
In your dual button, after or before the commands, add:
Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
Finally, in the places where the returned value of CanEecute is likely to change, add in your datacontext a
NotifyPropertyChanged(nameof(IsVIsible));
You should be on track with this.

Related

How do I properly set this button's binding to the parent ListView ItemsControl?

I have a custom button set up inside a ListView ItemTemplate. The Listview's ItemSource is bound to a collection of items, pretty standard. I have a few labels in the listview as well, and everything works fine except the button.
Binding the button to one of the properties won't work at all using {Binding buttonName} but it will sort of work if I use {Binding Items/buttonName, ElementName=listView} - the only problem is, when I do it this way, every single button in that listView will have the exact same buttonName.
Now the issue stems from my custom button's DataContext being set to Self; unfortunately, it has to be set to Self because the custom style I'm using needs this. If I try to change the button to a UserControl instead (with the button as a child, and the DataContext set on that), then I can't use the Command property of the button for some reason.
Here's a simplified version of my ListView making use of the custom button:
<ListView x:Name="listView" ItemsSource="{Binding MyPeopleData}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Label Content="{Binding PersonName}"/>
<ct:RevealButton Content="{Binding Items/recommendation, ElementName=listView}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As I said above, this will make every item in the listview use the same recommendation property rather than using it's own one.
If I try to use
<ct:RevealButton Content="{Binding recommendation}"/>
It just won't work, which makes sense given the DataContext of the custom button, which is below:
<Button x:Class="RevealButton" Width="{Binding Width}" Height="{Binding Height}" Background="{Binding ButtonBackground}" DataContext="{Binding RelativeSource={RelativeSource Self}}" Style="{DynamicResource ButtonRevealStyleC}" mc:Ignorable="d">
<Button.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{TemplateBinding Content}" />
</DataTemplate>
</Button.ContentTemplate>
</Button>
So this ended up being an XY Problem. Because the modified style I was using had a poorly bound property, it was failing when the parent control didn't have a DataContext set to self.
This is what it was:
<SolidColorBrush Opacity="0.8" Color="{Binding ButtonBackground.Color}" />
And ButtonBackground was a dependency property exposed by my custom Button, so binding the style in this way meant it only worked if the Button's context was itself. Changing it to this:
<SolidColorBrush Opacity="0.8" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ButtonBackground.Color}" />
Fixed the DataContext dependency, which in turn fixes the heirarchy of problems above.

WPF Set Visibility on DataTemplate UI Element from MVVM View Model

I have a control that is set up as a DataTemplate:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<DataTemplate x:Key="KEYBOARD_EN">
<StackPanel>
<Button Visibility="{Binding Path=RegisterButtonVisible}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
</StackPanel>
</DataTemplate>
In this DataTemplate there is a control on which I wish to set the Visibility from various view models:
<Button Visibility="{Binding Path=RegisterButtonVisible}" Style="{StaticResource ...} > Register </Button>
I do routed events with my control, so I tried to set up something similar, but no matter what I try, the RegisterButtonVisible property does not get picked up:
public partial class MainKeyboard : UserControl
{
public static DependencyProperty RegisterButtonVisibleProperty;
public Visibility RegisterButtonVisible
{
get { return (Visibility)GetValue(RegisterButtonVisibleProperty); }
set { SetValue(RegisterButtonVisibleProperty, value); }
}
static MainKeyboard()
{
RegisterButtonVisibleProperty = DependencyProperty.Register("RegisterButtonVisible", typeof (Visibility),
typeof (MainKeyboard));
}
}
In my ViewModel I do this:
public Visibility RegisterButtonVisible // get, set, raisepropchange, etc
My DataTemplate with the button in it is wrapped in a userControl:
<UserControl x:Class="Bleh.Assets.MainKeyboard"
x:Name="TheControl"
Unloaded="UserControl_Unloaded">
<Viewbox>
<Grid>
<ContentControl Name="ctrlContent" Button.Click="Grid_Click" />
</Grid>
</Viewbox>
and is used in my views like this:
<assets:MainKeyboard
RegisterButtonVisible="Collapsed"
Loaded="MainKeyboard_Loaded">
<b:Interaction.Triggers>
<b:EventTrigger EventName="Register">
<b:InvokeCommandAction Command="{Binding ConfirmEmailAddressCommand}"/>
</b:EventTrigger>
<b:EventTrigger EventName="Enter">
<b:InvokeCommandAction Command="{Binding EnterKeyCommand}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
</assets:MainKeyboard>
Please note this attribute:
RegisterButtonVisible="Collapsed"
This is my dependency property. It shows up in intelliesense, so the CLR has registered it correctly, but it does NOT pick up the property assignment (Collapsed is ignored).
This makes me feel like it is very close, but I do remember someone telling me I can not do this, thus the EventTriggers (this is a common issue with datatemplates and MVVM apparently).
So one option is to use something in the Interaction namespace, like I do my event triggers ( I just need to fire a "Visibility" trigger on this button somehow, at least I figure).
What is the right ANY way to do this in MVVM?
Fixing your code
In order to make your existing code work, you need to tell need to tell WPF what object RegisterButtonVisible should be read from. If it's a user control, give the UserControl a name and then reference that element via ElementName, like so:
<UserControl ... lots of stuff here
x:Name="TheControl"
>
In your button binding:
<Button Visibility="{Binding ElementName=TheControl, Path=RegisterButtonVisible}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
Of course, if you can't do that because the button and the usercontrol are in different files, you can still use an ancestor binding:
<Button Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type assets:MainKeyboard}},
Path=RegisterButtonVisible}"
Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
which, for each button, will walk up to find the closest instance of assets:MainKeyboard and then bind to the RegisterButtonVisible property.
Using MVVM
If you want to achieve the same using MVVM (instead of on a control), you need to use a converter to convert a boolean to a visibility property, like so:
<Button Visibility="{Binding IsRegistrationAllowed, Converter={StaticResource BoolToVis}}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
Of course, that assumes that your DataContext is set up correctly and pointing at your ViewModel.

Getting a tooltip in a user control to show databound text and stay open

I have a user control that shows a TextBox along with a small help icon.
My goal is to have a ToolTip pop-up, show some databound text and stay open when the mouse hovers over the help icon.
So, to that end I have created a HelpText dependency property in the user control allowing me to bind a help text string to the user control.
So, my user control looks something like this
<UserControl Name="textField" ...>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ElementName=textField,Path=Text}"/>
<Image Source="{StaticResource Help.Icon}">
<Image.ToolTip>
<ToolTip Content="{Binding ElementName=textField,Path=HelpText}"/>
</Image.ToolTip>
</Image>
</StackPanel>
</UserControl>
This code does show the tooltip, except that it is empty! Also, the StaysOpen property does not make any difference as the tooltip shuts down after a few seconds.
Funny thing is that when I set the same binding directly on the Image control's ToolTip property the bound text is shown allright in the tooltip pop-up, however it still does not stay open:
<Image Source="{StaticResource Help.Icon}" ToolTip="{Binding ElementName=textField,Path=HelpText}">
So my to questions are:
How come the binding against the user control's HelpText dependency property does not work in the first code sample but does work in the second?
How do I make the ToolTip stay open or rather how do I make the ToolTip both stay open and show the databound text?
Thanks!
ToolTips are not part of the same VisualTree as the rest of your XAML, so the DataContext is not inherited the way you would expect it to be.
Writing ToolTip="{Binding SomeProperty}" will automatically set the ToolTip's DataContext to SomeProperty, however if you build a custom ToolTip you must do this yourself.
<ToolTip DataContext="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}" ... />
This will bind the ToolTip's DataContext to the DataContext of whatever object the ToolTip is on.
To accomplish what you're trying to do, your <ToolTip> would probably look like this, since PlacementTarget would be your Image:
<!-- Could also use something like Tag if DataContext is actually used -->
<Image DataContext="{Binding ElementName=textField, Path=HelpText}"
Source="{StaticResource Help.Icon}">
<Image.ToolTip>
<ToolTip Content="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}"/>
</Image.ToolTip>
</Image>
As for why it won't stay open, I'm not positive but it might be because the ToolTipService.ShowDuration property defaults to 5 seconds, and that probably overwrites the StaysOpen property.
You can try setting it to something higher, such as
<Image ToolTipService.ShowDuration="60000" ... />
Or you can try this workaround of using a Popup styled to look like a ToolTip instead. The code would probably look something like this:
<Popup PlacementTarget="{Binding ElementName=MyImage}"
IsOpen="{Binding IsMouseOver, ElementName=MyImage, Mode=OneWay}">
<TextBlock Text="{Binding ElementName=textField, Path=HelpText}" />
</Popup>

How to bind a property of one element to a property of another in XAML

The following is some partial XAML:
<CheckBox Content="Display Data Points?" Margin="8,0.04,0,4" Grid.Row="1" FlowDirection="RightToLeft" d:LayoutOverrides="Height" HorizontalAlignment="Left"/>
and
<vf:DataSeries RenderAs="Line" DataSource="{Binding CdTeRoughnessList}" XValueType="DateTime" MarkerEnabled="{Binding ???}" Color="Red" LegendText="Roughness Average">
I would like to bind the MarkerEnabled property of the DataSeries to the IsChecked property of the CheckBox. In other words, when the user checks the check box, I want the MarkerEnabled to be set to True and False when unchecked.
Can this be done (I'm almost sure WPF would support this)? If so, how might I do it?
Give your Checkbox a name and then bind appropriately:
<CheckBox x:Name="DisplayDataCheckbox" Content="Display Data Points?"/>
<vf:DataSeries MarkerEnabled="{Binding ElementName=DisplayDataCheckbox, Path=IsChecked}">

Adjust the Visibility property of a View not working

I've a strange problem with binding a boolean property to a View's Visibility property.
I have a 'main' View that contains a bunch of other Views as well as various other UIElements including Buttons, TextBoxes, Grids, StackPanels and some telerik controls. Some of the controls have their visibility bound to boolean properties on my ViewModel, such that when the property is positive they are shown, and when negative they are collapsed.
<Border Visibility="{Binding IsSectionShown,
Converter={StaticResource BoolToVisibilityConverter}}" >
This is working perfectly for me. Obvious I have trigger the notification event in the IsSectionShown setter, and the control's Visibility is adjusted accordingly.
Now I have a View which needs to have its visibility adjusted. The obvious implementation is
<vw:ActivityView DataContext="{Binding Activity}"
Visibility="{Binding IsPositive,
Converter={StaticResource BoolToVisibilityConverter}}" />
Does not work! My work around is to wrap my view inside a StackPanel and adjust the visibility of the StackPanel - and this works fine:
<StackPanel Visibility="{Binding IsPositive,
Converter={StaticResource BoolToVisibilityConverter}}">
<vw:ActivityView DataContext="{Binding Activity}" />
</StackPanel>
Any ideas as to why this is happening? Workaround is fine but I would like to identity the gap in my understanding.
For both of these to work, the IsPositive property would have to exist both inside Activity and one level up in the data context that Activity comes from. But that's probably not what you intended. Instead, you can use something like this so that the visibility comes from a different data context than the one that applies to the view itself:
<vw:ActivityView DataContext="{Binding Activity}"
Visibility="{Binding IsPositive, ElementName=ParentElement,
Converter={StaticResource BoolToVisibilityConverter}}" />
where ParentElement is the parent element that contains vw:ActivityView.

Resources