Binding visibility to control in different class with WPF - wpf

In my main window xaml I have two user controls and two RadioButtons. I want the RadioButtons to control the Visibility of the user controls.
xaml excerpt:
<WpfApp2:ViewTree/>
<WpfApp2:ViewTab/>
<RadioButton x:Name="radioButton_Tree" GroupName="View"
IsChecked="True"> Tree View </RadioButton>
<RadioButton x:Name="radioButton_Tab" GroupName="View"
IsChecked="False" >Tab View</RadioButton>
in the user controls, I have something like this:
Visibility="{Binding IsChecked,
Converter={StaticResource BooleanToVisibilityConverter},
ElementName=Window1.radioButton_Tree}" >
At run time I get this error:
Cannot find source for binding with reference 'ElementName=Window1.radioButton_Tab'
What am I overlooking?

The name Window1 is not in the context of user control.
Can you use the code below?
<WpfApp2:ViewTree Visibility="{Binding IsChecked,
Converter={StaticResource BooleanToVisibilityConverter},
ElementName=radioButton_Tree}" />
<WpfApp2:ViewTab Visibility="{Binding IsChecked,
Converter={StaticResource BooleanToVisibilityConverter},
ElementName=radioButton_Tab}" />
<RadioButton x:Name="radioButton_Tree" GroupName="View"
IsChecked="True"> Tree View </RadioButton>
<RadioButton x:Name="radioButton_Tab" GroupName="View"
IsChecked="False" >Tab View</RadioButton>

Related

Popup doesn't bind to Toggle when inside DataGrid (xceed)

I've made a Toggle, which expands a Popup window with a ListBox inside. It looks like so:
<ToggleButton Name="Toggle" Height="20" Width="150" >
<StackPanel>
<TextBlock Text="TestListPopup"/>
<Popup Height="200" Width="150"
IsOpen="{Binding ElementName=Toggle, Path=IsChecked}"
PlacementTarget="{Binding ElementName=Toggle}"
HorizontalAlignment="Left"
VerticalAlignment="Bottom">
<ListBox SelectionMode="Multiple" SelectionChanged="TypeSelectionChanged" >
<ListBoxItem Content="Test1"/>
<ListBoxItem Content="Test2"/>
<ListBoxItem Content="Test3"/>
</ListBox>
</Popup>
</StackPanel>
</ToggleButton>
It works perfectly, but I want to use it inside the FilterRow of my xceed DataGrid here:
<xcdg:DataGridControl x:Name="dataGrid"
ItemsSource="{Binding Source={StaticResource DataSource}}">
<xcdg:DataGridControl.View>
<xcdg:TableflowView>
<xcdg:TableflowView.FixedHeaders>
<DataTemplate>
<xcdg:ColumnManagerRow/>
</DataTemplate>
<DataTemplate>
<xcdg:FilterRow>
<xcdg:FilterCell FieldName="Name" IsEnabled="True"/>
<xcdg:FilterCell FieldName="Type" IsEnabled="True">
<!-- TestListPopup control here -->
</xcdg:FilterCell>
</xcdg:FilterRow>
</DataTemplate>
</xcdg:TableflowView.FixedHeaders>
</xcdg:TableflowView>
</xcdg:DataGridControl.View>
<xcdg:DataGridControl.Columns>
<xcdg:Column FieldName="Name" Title="Name" />
<xcdg:Column FieldName="Type" Title="Type" Width="160"/>
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
In here though, the popup will not bind to the toggle button. Pressing the toggle button doesn't do anything.
I narrowed it down to the binding being broken, because if you set IsOpen="True", it's open (and not adhering to PlacementTarget), but again; it works perfectly outside of the DataGrid..
Why does a perfectly functional control break once put inside the FilterRow?
Any help is appreciated! :)
Why does a perfectly functional control break once put inside the FilterRow?
Because the ToggleButton and the FilterCell don't belong to the same namescope.
You may try to bind using an x:Reference:
IsOpen="{Binding Path=IsChecked, Source={x:Reference Toggle}}"
The other option would be to bind the IsChecked property of the ToggleButton to a bool property of a view model and also bind the IsOpen property of the Popup to the same source property. Make sure that the view model implements the INotifyPropertyChanged interface and raise change notifications when the source property is set.

ItemsControl items bindings called when collapsed

I have an ItemsControl which displays a list of messages. It's defined as ...
<ItemsControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding Messages}" >
</ItemsControl>
I then have a DataTemplate which handles the display for each message. It's defined as...
<DataTemplate DataType="{x:Type vm:MessageViewModel}">
<Button Command="{Binding CommandOpenPage}">
<Button.Template>
<ControlTemplate>
<Border Margin="2" BorderThickness="1"
BorderBrush="{Binding Flags, Converter={StaticResource msgFlagConverter}}"
Background="{Binding Flags, Converter={StaticResource msgFlagConverter}, ConverterParameter=1}" >
<TextBlock Text="{Binding Path=Message}" Style="{StaticResource ActionItem}" TextWrapping="Wrap" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
Everything displays OK. My problem is when the parent controls are set to Visibility=Collapsed my ItemsControl still goes through the DataTemplate and calls the converters for BorderBrush and BackgroundBrush for each MessageViewModel.
This is bothersome because when the list is very large the bindings are set and converters are executed when they shouldn't. This list is only visible when the user chooses to see it. I understood the binding engine ignores elements under a collapsed parent. Is there an exception to this rule? Or am I just missing something?
I found my problem. The above ItemsControl and DataTemplate were in a UserControl. The visibility was originally handled inside the usercontrol itself by binding the main layout grid to a visibility property. By simply setting the user controls visibility in the parent XAML all bindings started behaving as expected.
This fixes my problem but I still don't understand the difference between setting the visibility of the main layout grid vs the visibility of the usercontrol itself.
<c:ApplicationMenuView Grid.Column="1" Grid.Row="4"
HorizontalAlignment="Left" Margin="1"
VerticalAlignment="Stretch"
DataContext="{Binding Menu}"
Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>

Make TabItem select first nested button when clicked (WPF MVVM)

As the title says, I would like the first nested Button to be selected (this button selects a view) when the tab item is clicked. Here is my code below:
<TabItem Header="Scheduling">
<StackPanel Style="{StaticResource ResourceKey=TabStackPanelStyle}">
<RadioButton Command="{Binding BookResourceCommand}" Style="{StaticResource ResourceKey=TabButtonStyle}">Book</RadioButton>
<RadioButton Command="{Binding NewResourceCommand}" Style="{StaticResource ResourceKey=TabButtonStyle}">New</RadioButton>
<RadioButton Command="{Binding EditResourceCommand}" Style="{StaticResource ResourceKey=TabButtonStyle}">Edit</RadioButton>
<RadioButton Command="{Binding DeleteResourceCommand}" Style="{StaticResource ResourceKey=TabButtonStyle}">Delete</RadioButton>
</StackPanel>
</TabItem>
This TabItem sits in a TabControl with a few more similar TabItems. All I want to do is have the TabItem select the first RadioButton (by default) when it is clicked. These radio buttons change a user control in my ViewModel.
I know it would be possible using EventTriggers associated with the TabItem but there must be a better way.
Thanks!
I think in this situation you can use Binding:
TabItem
<TabItem x:Name="MyTabItem" Header="Two">
<Label Content="Some Content" />
</TabItem>
RadioButton
<RadioButton Name="MyButton" Content="Two" IsChecked="{Binding ElementName=MyTabItem, Path=IsSelected}" />
If you want to be when you click on the RadioButton, the tab is not selected, use Mode=OneWay:
<RadioButton Name="MyButton" IsChecked="{Binding ElementName=MyTabItem, Path=IsSelected, Mode=OneWay}" />

Pattern for working with a form in Silverlight 4? (How to get references to XAML elements)

I have a form:
<StackPanel Orientation="Horizontal" Visibility="{Binding Editable, Converter={StaticResource visibilityConverter}}"
ToolTipService.ToolTip="Add new topic to this group">
<sdk:AutoCompleteBox Width="160" ItemsSource="{Binding ElementName=LayoutRoot, Path=DataContext.TopicNames}" />
<Button Click="addTopicButton_Click">
<Image Source="Images/appbar.add.rest.png" />
</Button>
</StackPanel>
This form appears in a DataTemplate for an ItemsControl. I'm not sure what the best way is to get the data from the AutoCompleteBox when the button is clicked. I can't give the elements x:Name attributes, because they're in a template (right?).
How can I get around this? The Click event will give me the Button, but I need a reference to the text box. Use the Button's parent, then look through the children for the Textbox? If I factored this out into its own UserControl, I could set x:Name values, but I'd rather not do that.
Any other ideas?
Update: Here is another example of such a problem:
<ListBox x:Name="topicList"
ItemsSource="{Binding Id, Converter={StaticResource topicGroupIDConverter}}"
SelectionChanged="ListBox_SelectionChanged"
HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
Width="150"
VerticalAlignment="Center"
ToolTipService.ToolTip="{Binding Description}"
ToolTipService.Placement="Right" />
<Button ToolTipService.ToolTip="Remove this topic from this group"
Visibility="{Binding ElementName=topicList,
Path=DataContext.Editable,
Converter={StaticResource visibilityConverter}}"
Click="removeTopicButton_Click"
HorizontalAlignment="Right"
Margin="10,0">
<Image Source="Images/appbar.cancel.rest.png" />
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When the button is clicked, I want to access topicList.DataContext. However, topicList itself is a DataTemplate in an ItemsControl, so I can't access it using its name from code-behind. How else can I do this?
You can add a property, say SelectedItemInAutoCompleteBox, to your presenter, and then can bind it to the SelectedItem property of AutoCompleteBox, using Mode=TwoWay, like this,
<sdk:AutoCompleteBox SelectedItem="{Binding Path=DataContext.SelectedItemInAutoCompleteBox, Mode=TwoWay}" ... />
You may try the same approach with Text property of AutoCompleteBox, also. See if it solves your problem.:-)
You have several choices:
If you're on Silverlight 5, use the AncestorBinding
Otherwise, use a Silverlight 4 AncestorBinding hack (it doesn't look pretty)
Or you could try DataContextProxy, which stores the DataContext in a resource so that it is accessible. Note: you should set the DataContextProxy as a Resource of topicList ListBox, not the UserControl as in Dan Wahlin's example.

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