Silverlight ZOrder doesn' work for me in Bing control - silverlight

I'm trying to make my popup widget to be on top in a map but setting Canvas.ZOrder doesn't help.
Here is XAML:
<m:Map x:Name="MainMap"
Margin="0,6,3,3"
ZoomLevel="{Binding MapZoomLevel, Mode=TwoWay}"
Center="{Binding MapCenter, Mode=TwoWay}"
CopyrightVisibility="Collapsed"
CredentialsProvider="{Binding BingCredentialsProvider}"
UseInertia="True"
Mode="Road" Grid.Column="2" Grid.Row="1">
<m:MapItemsControl ItemsSource="{Binding Source={StaticResource WorkLayerData}}">
<m:MapItemsControl.ItemTemplate>
<DataTemplate>
<Canvas
m:MapLayer.Position="{Binding Location}">
<Button
Width="{Binding PushpinWidth}" Height="{Binding PushpinWidth}"
Margin="{Binding PushpinMargin}"
Style="{StaticResource LooklessButtonStyle}"
Command="{Binding DataContext.SelectedPushpinChangedCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding}"
Cursor="Hand">
<Ellipse
Width="{Binding PushpinWidth}" Height="{Binding PushpinWidth}" Stroke="Black" Fill="{Binding IsGPSDataRecent, Converter={StaticResource BoolToGreenRedBrushConverter}}" StrokeThickness="1">
<ToolTipService.ToolTip>
<TextBlock Text="{Binding DeviceId}" />
</ToolTipService.ToolTip>
</Ellipse>
</Button>
<!-- Show black dot over actual GPS point -->
<Ellipse
Width="10" Height="10" Stroke="Black" Fill="Black" StrokeThickness="1"
Margin="-5,-5,0,0"
Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}" />
<Border
Width="200"
BorderThickness="1" BorderBrush="DarkGray"
Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Color="#FF000000" Opacity="0.5" ShadowDepth="2" />
</Border.Effect>
<ContentControl Template="{StaticResource TrackedAssetControlTemplate}" />
</Border>
</Canvas>
</DataTemplate>
</m:MapItemsControl.ItemTemplate>
</m:MapItemsControl>
</m:Map>
I tried to set ZIndex on a border but no luck.
Here is how it looks when IsSelected = true (see other dots with ZIndex higher on top)

In order to bring an item in a MapItemsControl to front it is necessary to set the ZIndex of the item container. You can do that in code behind by retrieving the item container from the MapItemsControl's ItemContainerGenerator.
In case you don't want that, you could apply an attached helper property to the top-level container (the Canvas) in the DataTemplate for your map items. As this Canvas is the direct child of the item container, the helper property would have to set the ZIndex of the visual parent of the Canvas. If that sounds weird, here is the code for the attached property, in a helper class called MapItem:
public class MapItem
{
public static readonly DependencyProperty ZIndexProperty =
DependencyProperty.RegisterAttached("ZIndex", typeof(int),
typeof(MapItem), new PropertyMetadata(ZIndexChanged));
public static int GetZIndex(DependencyObject obj)
{
return (int)obj.GetValue(ZIndexProperty);
}
public static void SetZIndex(DependencyObject obj, int value)
{
obj.SetValue(ZIndexProperty, value);
}
private static void ZIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// set ZIndex on parent of obj
Canvas.SetZIndex((UIElement)VisualTreeHelper.GetParent(obj), (int)e.NewValue);
}
}
In your DataTemplate you may now bind the helper property to one of your VM properties, perhaps by using an appropriate binding converter:
<DataTemplate x:Key="MapItemDataTemplate">
<!-- setting the helper property MapItem.ZIndex on Canvas
sets the Canvas.ZIndex property on the item container -->
<Canvas local:MapItem.ZIndex="{Binding ...}">
...
</Canvas>
</DataTemplate>

Related

How to make tooltip appear at fixed position (in this case: bottom left corner of page)

I have several items (in the form of rectangles) inside a "Carousel" (which again is basically just a fancy PathListBox)
When the mouse hovers over one of those rectangles, a tooltip with some information appears. The look of the rectangles is determined by a DataTemplate. The beginning of the xaml of this DataTemplate is displayed here:
<Carousel:CarouselControl.DataTemplateToUse>
<DataTemplate>
<Grid ShowGridLines="True">
<Grid.ToolTip>
<ToolTip Placement="Right"
PlacementRectangle="50,0,0,0"
HorizontalOffset="10"
VerticalOffset="20"
HasDropShadow="false"
PlacementTarget="{Binding ElementName=Box512}"
>
<BulletDecorator>
<TextBlock Text="{Binding Text}"/>
</BulletDecorator>
</ToolTip>
</Grid.ToolTip>
//further xaml code defining the DataTemplate
Just for completeness, this is the xaml for "Box512":
<Border x:Name="Box512" Grid.Row="2" Grid.Column="1" Grid.RowSpan="4"
Grid.ColumnSpan="2" BorderThickness="0" Opacity="0.5">
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="Resources/Box512.ico"/>
</Style>
</Image.Style>
</Image>
</Border>
The tooltip is supposed to be displayed at a fixed location in the bottom left corner of the page close to a XAML element with the name "Box512".
To this end I used the "PlacementTarget"-Property of the tooltip. I tried (as you can see in the code above):
PlacementTarget="{Binding ElementName=Box512}"
This didn't work. INstead of in the left bottom corner of the page, the tooltip was still displayed at the rectangle over which the mouse hovered. I then tried:
PlacementTarget="{x:Bind Box512}"
... this didn't work either.
So my question is: how can I make the tooltip appear at the same position near Box512 independent of which rectangle my mouse is hovering over?
****************************ADDITIONAL INFORMATION***********************
MainWindow.xaml:
<Carousel:CarouselControl x:Name="CarouselControl"
ScaleRange="1.0,1.0"
MaxNumberOfItemsOnPath="4"
SelectedItem="{Binding CurrentData,Mode=TwoWay}"
SelectionChanged="CarouselControl_SelectionChanged"
ItemsSource="{Binding GraphItems}"
CustomPathElement="{Binding ElementName=customPath}">
<Carousel:CarouselControl.DataTemplateToUse>
<DataTemplate>
with this line of code the text inside the tooltip would get displayed BUT
the tooltip would not be in the desired location:
<!--<Grid ShowGridLines="True">-->
with this line of code the tooltip is empty but the tooltip
is displayed in the desired location:
<Grid ShowGridLines="True" ToolTipService.PlacementTarget="{Binding
ElementName=Box512}">
<Grid.ToolTip>
<ToolTip Placement="Right"
PlacementRectangle="50,0,0,0"
HorizontalOffset="10"
VerticalOffset="20"
HasDropShadow="false">
<BulletDecorator>
<BulletDecorator.Bullet>
<Ellipse Height="10" Width="20" Fill="Blue"/>
</BulletDecorator.Bullet>
<TextBlock Text="{Binding Text}"/>
</BulletDecorator>
</ToolTip>
</Grid.ToolTip>
//further xaml code defining the DataTemplate
</Grid>
</DataTemplate>
</Carousel:CarouselControl.DataTemplateToUse>
</Carousel:CarouselControl>
MainWindow.cs:
public partial class MainWindow : Window
{
///View Model for MainWindow.
private ViewModels.MainWindowVM mainViewModel = null;
public MainWindow()
{
InitializeComponent();
//Initialize View Model
mainViewModel = new ViewModels.MainWindowVM();
//Set it as Data Context
this.DataContext = mainViewModel;
//Close-Event bound
mainViewModel.RequestClose += delegate
(object sender, EventArgs e) { this.Close(); };
}
}
The GraphItems list is defined as a property of the MainViewModel:
private List<GraphNode> _GraphItems;
public List<GraphNode> GraphItems
{
get { return _GraphItems; }
private set
{
_cGraphItems = value;
this.RaisePropertyChangedEvent("GraphItems");
}
}
The "Text" Property is associated with the GraphNode class:
GraphNode.cs:
public class GraphNode : ObservableObject
{
///GENERAL INFORMATION
private string _Text;
public string Text {
get { return _Text; }
set { _Text = value; this.RaisePropertyChangedEvent("Text"); }
}
//more code
}
You should use the TooltipService to specify the placement target:
<TextBlock Text="Header" Grid.Row="0" Background="Green" Margin="0,0,0,0" ToolTipService.PlacementTarget="{Binding ElementName=Box512}">
<TextBlock.ToolTip>
<ToolTip Placement="Right"
PlacementRectangle="50,0,0,0"
HorizontalOffset="10"
VerticalOffset="20"
HasDropShadow="false"
>
<BulletDecorator>
<TextBlock Text="This is the tooltip text"/>
</BulletDecorator>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>

PropertyChanged value is null for child ViewModel property

I have an ObservableCollection of ViewModels in my main ViewModel. The binding seems to work fine since I can switch views. However, raising the ViewModelBase OnPropertyChanged method (which work for other stuff) in an element of the ObservableCollection result in a null PropertyChanged value in ViewModelBase.
Here's my main code snippets:
In my main ViewModel Constructor:
public EditorViewModel()
{
base.DisplayName = Strings.EditorName;
_availableEditors = new ObservableCollection<ViewModelBase>();
AvailableEditors.Add(new GBARomViewModel(646, 384));
AvailableEditors.Add(new MonsterViewModel(800, 500));
CurrentEditor = _availableEditors[0];
}
At GBA ROM loading, ViewModel and Model properties are updated:
void RequestOpenRom()
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.DefaultExt = ".gba";
dlg.Filter = "GBA ROM (.gba)|*.gba|All files (*.*)|*.*";
dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
if(CurrentEditor is GBARomViewModel)
{
(CurrentEditor as GBARomViewModel).ReadRom(dlg.FileName);
}
}
}
In my main View: Variation of TabControl (to have view switching and view states preservation).
<controls:TabControlEx ItemsSource="{Binding AvailableEditors}"
SelectedItem="{Binding CurrentEditor}"
Style="{StaticResource BlankTabControlTemplate}"
MinWidth="{Binding CurrentEditorWidth}"
MinHeight="{Binding CurrentEditorHeight}"
MaxWidth="{Binding CurrentEditorWidth}"
MaxHeight="{Binding CurrentEditorHeight}"
Width="{Binding CurrentEditorWidth}"
Height="{Binding CurrentEditorHeight}"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<controls:TabControlEx.Resources>
<DataTemplate DataType="{x:Type vm:GBARomViewModel}">
<vw:GBARomView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MonsterViewModel}">
<vw:MonsterView />
</DataTemplate>
</controls:TabControlEx.Resources>
</controls:TabControlEx>
In GBARomViewModel (child ViewModel, element of AvailableEditors)
public String CRC32
{
get
{
return _rom.CRC32;
}
set
{
if (value.Equals(_rom.CRC32))
{
return;
}
_rom.CRC32 = value;
OnPropertyChanged("CRC32");
}
}
Property binding in child View
Now this is a UserControl so I'll put its code as well after. Other properties at startup work such as LabelWidth and the LabelValue. Giving a default value to TextBoxValue in XAML also work.
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="10, 0, 0, 10" Width="300">
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomTitle}" TextBoxValue="{Binding Title}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomGameCode}" TextBoxValue="{Binding GameCode}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomRomSize}" TextBoxValue="{Binding RomSize}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomCRC32}" TextBoxValue="{Binding CRC32}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="200" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomMD5Checksum}" TextBoxValue="{Binding MD5Checksum}"/>
</StackPanel>
DefaultLabelBox.cs
<UserControl x:Name="uc">
<StackPanel>
<TextBlock Text="{Binding Path=LabelValue, ElementName=uc}"
Width="{Binding Path=LabelWidth, ElementName=uc}"/>
<Label Content="{Binding Path=TextBoxValue, Mode=OneWay, ElementName=uc}"
Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
</StackPanel>
</UserControl>
DefaultLabelBox.xaml.cs
public string TextBoxValue
{
get {
return (string)GetValue(TextBoxValueProperty);
}
set {
SetValue(TextBoxValueProperty, value);
}
}
public static readonly DependencyProperty TextBoxValueProperty =
DependencyProperty.Register("TextBoxValue", typeof(string), typeof(DefaultLabelBox), new PropertyMetadata(default(string)));
Control Template
<Style TargetType="dlb:DefaultLabelBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="dlb:DefaultLabelBox">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding LabelValue, RelativeSource={RelativeSource TemplatedParent}}"
MinWidth="20"
Width="{Binding LabelWidth, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center"
FontFamily="Mangal"
Height="20"
FontSize="13"/>
<Label Content="{Binding TextBoxValue, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
BorderBrush="{StaticResource DefaultLabelBoxBorderBrush}"
BorderThickness="1"
Padding="1,1,1,1"
Background="{StaticResource DefaultLabelBoxBackgroundBrush}"
Foreground="{StaticResource DefaultLabelBoxForeground}"
MinWidth="60"
Height="20"
VerticalAlignment="Center"
FontFamily="Mangal"
Width="{Binding TextBoxWidth, RelativeSource={RelativeSource TemplatedParent}}"
FontSize="13"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I tried a few things but being new to MVVM I don't know if I have a DataContext issue of a binding one. Any help would be appreciated.
Edit: I made changes to some code to illustrate the working solution for me as well as adding the ControlTemplate I had forgot. I'm not sure if Mode=OneWay is mandatory in UserControl and ControlTemplate but it's working now so I'm leaving it as it is.
In order to make a binding like
<dlb:DefaultLabelBox ... TextBoxValue="{Binding CRC32, Mode=TwoWay}" />
work, the DefaultLabelBox needs to inherit its DataContext from its parent control (this is btw. the reason why a UserControl should never explicitly set its DataContext).
However, the "internal" bindings in the UserControl's XAML then need an explicitly specified Source or RelativeSource or ElementName.
So they should (for example) look like this:
<UserControl ... x:Name="uc">
<StackPanel>
<TextBlock
Text="{Binding Path=LabelValue, ElementName=uc}"
Width="{Binding Path=LabelWidth, ElementName=uc}"/>
<TextBox
Text="{Binding Path=TextBoxValue, Mode=TwoWay, ElementName=uc}"
Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
</StackPanel>
</UserControl>

How to set image path of specific ityem in ListView?

I have a ListView of Button elements like this:
<ListView ItemsSource="{Binding NumberOfItems}" SelectedItem="{Binding SelectedItem}">
<ListViewItem >
<Button Name="test" Grid.Row="0" Grid.Column="10" Grid.ColumnSpan="4" Grid.RowSpan="4" VerticalAlignment="Center" Background="Transparent" Command="{Binding DataContext.TestCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}">
<Button.Template>
<ControlTemplate>
<Grid RenderTransformOrigin="0.5,0.5" x:Name="bg">
<Image Source="{Binding DataContext.Test_ImagePath, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</ListViewItem >
</ListView>
My goal is to have button's image to toggle between two image paths on button click. It works, but the problem is that all the buttons in the list change the image path on some button click. I want only the one that is clicked to change the image path. I tried using CommandTarget property like this:
CommandTarget="{Binding DataContext.Listview.SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}
but it didn't heplp.
Just to mention that I use MVVM.
How to solve this?
If you are using MVVM, I suppose you could wrap your models (As you said, integers for now) with a wrapper like this:
public class ToggleableWrapper<T> : INotifyPropertyChanged {
private bool toggled;
public ToggleableWrapper(T item){
this.Item = item;
this.ClickCommand = new RelayCommand(() => this.Toggled = !this.Toggled);
}
public T Item {get;}
public ICommand ClickCommand {get;}
public bool Toggled {
get { return this.toggled; }
set {
this.toggled = value;
OnPropertyChanged(nameof(this.Toggled));
}
}
//Property changed implementation...
}
So your NumberOfItems collection could look like this:
public ObservableCollection<ToggleableWrapper<int>> NumberOfItems {get;}
Now you need a ValueConverter which will convert the toggled boolean to your image. Call it ToggledToImageConverter
You can implement it accordingly and make it a resource somewhere.
Now your ListView looks like this:
<ListView ItemsSource="{Binding NumberOfItems}" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Name="test" Grid.Row="0" Grid.Column="10" Grid.ColumnSpan="4" Grid.RowSpan="4" VerticalAlignment="Center" Background="Transparent" Command="{Binding ClickCommand}">
<Button.Template>
<ControlTemplate>
<Grid RenderTransformOrigin="0.5,0.5" x:Name="bg">
<Image Source="{Binding Toggled, Converter={StaticResouce ToggledToImageConverter}"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
<DataTemplate>
</ListView.ItemTemplate>
</ListView>
So when you click the button, the bool is toggled, which will then toggle the image using the ValueConverter.

UserControl child fill Window

I have a UserControl that is a portion of a wpf window.
<Window>
<Grid>
<!--some other display elements would be here-->
<local:MyUserControl x:Name="Foo" Padding="0,42,0,50"/>
</Grid>
</Window>
Inside MyUserControl I have an element that is a gallery that is normally hidden, but when visible, it should fill the entire screen.
<UserControl>
<Grid>
<!--main display elements would be here-->
<Grid Name="Gallery" Visibility="Hidden">
<Rectangle Fill="Black" Opacity="0.75"/>
<TextBlock Name="GalleryLabel" Foreground="White" TextAlignment="Center">Current Image Title</TextBlock>
<Button Name="CloseGallery" Style="{DynamicResource WhiteTextButton}" Margin="0,0,0,0" Height="25" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Click="GalleryClose_OnClick">X</Button>
<Image Name="GalleryImage" Margin="25"/>
</Grid>
</Grid>
</UserControl>
How can I set the Gallery to fill the entire Window rather than just the UserControl?
I was able to get it to work by adding Margin="0,-42,0,-50" to Gallery, but I don't like that solution. I would rather do something that doesn't involve hard-coding values in the UserControl so that I would be able to have more flexiblility in using it.
Normally it looks like:
where the green Foo area is MyUserControl, and the rest of the things in the window are other elements.
At certain points, I have a gallery display an image, which should fill the entire screen like:
which should fill the entire screen and have a black opaque overlay, along with an image displayed on top of the overlay.
Remove the Padding...
You can use:
<local:MyUserControl x:Name="Foo" VerticalAlignment="Stretch" HorizontalAligment="Stretch"/>
EDIT
I may admit that I still am not sure what you want. But I made something which does the same as you showed in the pictures. But be aware that this as an opinion based question, and the answer is my opinion, there are a lot of ways to achieve this. And this is only one of them:
MainWindow
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myProject="clr-namespace:MyProject"
Title="MainWindow" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Background="Gainsboro" Grid.Row="0">
<StackPanel Orientation="Horizontal">
<Button Content="Show gallery" Width="100" Margin="10"/>
<Button Content="Do something else" Width="150" Margin="10"/>
</StackPanel>
</Border>
<myProject:SomeOtherStuff Grid.Row="1" />
<Border Grid.Row="2" Background="Gainsboro">
<StackPanel Orientation="Horizontal">
<Label Content="Buttom area, can be used for something else"/>
</StackPanel>
</Border>
<myProject:GalleryUserControl x:Name="GalleryUserControl" Grid.Row="0" Grid.RowSpan="3" Visibility="Hidden"/>
</Grid>
SomeOtherStuff UserControl
<UserControl x:Class="MyProject.SomeOtherStuff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="Green">
<Label VerticalAlignment="Center" HorizontalAlignment="Center" Content="Foo" FontSize="30" FontFamily="Verdana"/>
</Grid>
Gallery UserControl
<UserControl x:Class="XamDataGrid.GalleryUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="500" Width="800">
<Grid Background="#99000000">
<TextBlock Text="Currect image title" Foreground="White" TextAlignment="Center" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<Button Name="CloseGallery" Margin="0,0,0,0" Content="X" Height="25" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Click="GalleryClose_OnClick"/>
<Image Margin="30"/>
</Grid>
An Adorner will be best for you. As it floats above everything else and remains outside the VisualTree too.
UserControl2
<UserControl2 ...>
<Grid>
<Grid Background="#FF709FA6" Opacity="0.3" Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
</Grid>
<Grid Width="600" Height="700">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF37DAEA" Offset="0"/>
<GradientStop Color="#FFE84242" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<!-- Gallery controls -->
<Button Content="Welcome to Gallery" HorizontalAlignment="Left" IsHitTestVisible="True" Margin="112,126,0,0" VerticalAlignment="Top" Height="68" FontSize="48" Click="Button_Click_1"/>
<TextBlock HorizontalAlignment="Left" Margin="83,42,0,0" TextWrapping="Wrap" Text="UserControl2" VerticalAlignment="Top" Foreground="#FF1B0D0D"/>
</Grid>
</Grid>
</UserControl2>
UserControl1
Code :
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// get root Window
DependencyObject current = LogicalTreeHelper.GetParent(this);
while (!(current is Window))
current = LogicalTreeHelper.GetParent(current);
Window root = current as Window;
IEnumerable children = LogicalTreeHelper.GetChildren(root);
Panel p = null;
foreach (var child in children)
{
p = child as Panel; // get first container panel in Window
break;
}
// Create UserControl2 to add to the adorner
UserControl2 ctrlGallery = new UserControl2();
AdornerLayer layer = AdornerLayer.GetAdornerLayer(p);
GalleryAdorner adorner = new GalleryAdorner(p, ctrlGallery);
layer.Add(adorner);
}
}
public class GalleryAdorner : Adorner
{
private Control _child;
VisualCollection collection;
public GalleryAdorner(UIElement elem, Control child)
: base(elem)
{
collection = new VisualCollection(this);
_child = child;
collection.Add(_child);
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
protected override Visual GetVisualChild(int index)
{
if (index != 0) throw new ArgumentOutOfRangeException();
return collection[0];
}
protected override Size MeasureOverride(Size constraint)
{
_child.Measure(constraint);
return _child.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
_child.Arrange(new Rect(new Point(0, 0), finalSize));
return new Size(_child.ActualWidth, _child.ActualHeight);
}
}

WPF Treeview - Get status of checkbox

I have created a Treeview and used a stack panel to include a checkbox, icon image and text for each node in the tree.
These nodes are created at runtime.
I also have a button object.
The xaml is below.
The problem i have is that, when the click me button is clicked, i need to traverse thru the tree view and if a checkbox is checked, perform some function.
Does anyone know how to find out if the checkbox for a node in the tree is checked, from the C# code behind ???
<Window x:Class="WPF_Explorer_Tree.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF_Explorer_Tree"
Title="KryptoG" Height="424" Width="815" Loaded="Window_Loaded">
<Window.Resources>
<local:HeaderConverter x:Key="formatter" />
</Window.Resources>
<Grid>
<TreeView x:Name="foldersItem" SelectedItemChanged="foldersItem_SelectedItemChanged" Background="#FFFFFFFF" BorderBrush="#FFFFFFFF" Foreground="#FFFFFFFF" Margin="0,0,236,112" AllowDrop="True" Visibility="Visible">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Name="ST" Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" Name="SelectedCheckBox" IsChecked="False" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
<Image Name="img" Width="20" Stretch="Fill"
Source="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type TreeViewItem}},
Path=Header,
Converter={x:Static local:HeaderToImageConverter.InstanceIcon}}"
/>
<TextBlock VerticalAlignment="Center" Text="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type TreeViewItem}},
Path=Header,
Converter={StaticResource formatter}}"
/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
</TreeView>
<TreeView HorizontalAlignment="Right" Margin="0,0,12,12" Name="treeView1" Width="204" AllowDrop="True" BorderBrush="White" Foreground="White" />
<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,70" Name="button1" VerticalAlignment="Bottom" Width="75" Click="button1_Click">Click Me</Button>
<Button Height="23" HorizontalAlignment="Left" Margin="267,0,0,69" Name="button2" VerticalAlignment="Bottom" Width="75" Click="button2_Click">Click Me too</Button>
</Grid>
I would create a Two-Way data binding with that Check Box's IsChecked property to a ViewModel object instead. Much easier than navigating the tree.
Edit (per request of person asking):
Here's an example View Model (very simple that is only accounting for the IsChecked property):
public class ViewModel : System.ComponentModel.INotifyPropertyChanged
{
private bool? _isChecekd;
public bool? IsChecked
{
get { return _isChecekd; }
set
{
if (_isChecekd != value)
{
_isChecekd = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("IsChecked"));
}
}
}
}
#region INotifyPropertyChanged Members
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
#endregion
}
Now that you have an object implementing INotifyPropertyChanged, you can bind UI element properties to them. So you would update the IsChecked property of your CheckBox to this property. To do that, you first have to set the DataContext of Window1 in some way (or you could just do this on the TreeView itself as well). In your Window1.xaml.cs:
public Window1()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
Then, in your Window1.xaml file, update the CheckBox IsChecked property:
<CheckBox VerticalAlignment="Center" Name="SelectedCheckBox" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" />
And then, in whatever code you need to be able to interrogate the currently value of IsChecked, you can get to it this way (assuming this is Window1):
((ViewModel)this.DataContext).IsChecked
Hope that helps!
I think Tony Heupel's answer is the best approach, but to understand it you need to know about the MVVM (Model-View-ViewModel) design pattern. I suggest you read this excellent article by Josh Smith

Resources