I am making a TreeView with multiple levels of information being displayed. What I am hoping to achieve, is to show a different view when the sub-tree is expanded and closed. So, when just looking at the list, I want to make a view which shows a brief status overview of my more detailed view that would be visible if you expanded that item in the list.
I know I could just create 2 viewmodels implementing the same Interface, and based on a boolean IsExpanded, I could set the ActiveViewModel to one or the other, but I was just curious if I could just have the one viewmodel and change its view based on that boolean instead to save memory.
-OR-
Alternatively, should I just put 2 StackPanels into the same View, and then bind a visibility to be inverse of each other, so only one can be shown at a time?
-CODE-
Here is my current code (Private information removed / generic representation):
Xaml:
<UserControl.Resources>
<Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="5"></Setter>
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{StaticResource Univers57}"/>
<Setter Property="FontSize" Value="20" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</UserControl.Resources>
<TreeView x:Name="TreeView"
HorizontalAlignment="Stretch"
ItemsSource="{Binding ItemViewModels}"
ItemContainerStyle="{StaticResource TreeViewItemStyle}">
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type viewModel:ItemViewModel}" ItemsSource="{Binding Tasks}">
<StackPanel Orientation="Horizontal" Visibility="{Binding IsVisible}>
<Image Source="../../Images/Image.png" Height="24" Width="24"/>
<TextBlock Text="{Binding Item.Name}" />
<Button Style="{StaticResource ButtonStyle}">Dispatch</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" Visibility="{Binding IsOtherVisible}>
<Image Source="../../Images/Image2.png" Height="24" Width="24"/>
<TextBlock Text="{Binding Item.Name}" />
<Button Style="{StaticResource ButtonStyle}">Dispatch</Button>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type viewModel:TaskViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Task.Name}" Width="200"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
Both the ItemViewModel and TaskViewModel inherit from 'TreeViewModel' which implements INotifyPropertyChanged, and has IsExpanded and IsSelected.
IsVisible on the StackPanel Binding should be set based on IsExpanded's value. (It only shows up once you expand the item. So, one stackpanel or the other should show up).
I have just had a play with the WPF Visual Tree tools in VS2015 and it looks like the IsExpanded isnt being changed when I expand/collapse the tree items. It only sets a value during creation of the viewmodels, after that it will never change - even though they physically open and close when running the program.
Have managed to find a solution.
Firstly, needed to set 2 Way Binding on the binding to IsExpanded.
Secondly, I have overridden / hidden IsExpanded by declaring it as new in the derived class.
Now when set() gets called on IsExpanded, I can make a change to the IsVisible and IsOtherVisible before sending OnPropertyChange(); Now that I can change the IsVisible's, they can fire their own OnPropertyChange() and all is good in the world again.
<Style TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border BorderBrush="Red" CornerRadius="2" />
<ScrollViewer x:Name="PART_ContentHost" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Label x:Name="watermarklabel" Height="40" Content="{TemplateBinding Tag}" Foreground="Gray"/>
</Grid>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="True">
<Condition.Binding>
<MultiBinding Converter="{StaticResource Multi}">
<!--<Binding Path="Text" ElementName="txt1"/>-->
<Binding RelativeSource="{RelativeSource Mode=TemplatedParent}" Path="Text"/>
</MultiBinding>
</Condition.Binding>
</Condition>
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" TargetName="watermarklabel" Value="Collapsed"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here i want to enable/disable the label (which is water mark) upon the condition when the textbox text is empty and not empty. In the multi binding i am trying to access the text property using the TemplatedParent. But it is not hitting convertor when text changed.
When i use the element name to access it everything is fine.
But i want to make generic this one..
How to make this to work?
Thanks in advance..
This is actually surprisingly tricky to get right. There are a dozen different almost-right answers on the internet, but few or no completely correct ones.
The closest I've ever come to getting this to work correctly is to dynamically inject a textblock into the standard textbox's ControlTemplate at runtime based on the conditions that you're evaluating.
Dynamic injection avoids having to re-implement the entire controltemplate and, assuming the control template doesn't transform too much between versions, also avoids the maintenance point of having to create new ControlTemplates every time built-in theming changes.
Injecting an element instead of modifying the existing text element avoids all kinds of problems where users can select/manipulate the watermark text in undesirable ways.
Avoid an overlaying solution: the z-order and clipping issues aren't worth the effort (I once tried to do this with a decorator, not the right call)
Don't forget to verify that Drag/Drop and Copy/Paste operations on the text box work as expected with the watermark.
IIRC, I did it with an attached property so that a watermark didn't require a new control. Rather, it searched the visual tree for the first viable watermark target it could find and applied the watermark there. This let it work in a combobox as well, e.g., without add'l work.
I created some assets in inkscape and would like to use them as icons in a windows 8 application. I have done some reading and it seams that while .Net 4.5 supports SVG, the modern ui profile does not. I converted the svg to xaml using this tool.
I get the following xaml.
<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="svg2997" Width="744.09448" Height="1052.3622" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Canvas x:Name="layer1">
<Path Fill="#FFCCCCCC" Stroke="#FF000000" StrokeThickness="1.34377062" StrokeMiterLimit="4" x:Name="path3007" Data="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z" />
</Canvas>
</Canvas>
If I add this directly to my apps xaml it will render however the scale is way off.
I would like to use this as an image source for an image object if possible.
<Image HorizontalAlignment="Left" Height="100" Margin="127,37,0,0" VerticalAlignment="Top" Width="100" Source="Assets/plus_circle.xaml"/>
Can this be done?
Most AppBar buttons are based on a style included in StandardStyles called AppBarButtonStyle.
To customize the text of the button you set the AutomationProperties.Name attached property, to customize the icon in the button you set the Content property, and it's also a good idea to set the AutomationProperties.AutomationId attached property for accessibility reasons.
Here's an example of a button customized using this approach:
<Style x:Key="FolderButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="FolderAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Folder"/>
<Setter Property="Content" Value=""/>
</Style>
As mentioned above, to customize the icon you set the Content property. The challenge is how you set the content so it displays your custom vector art.
It turns out you can place any path Xaml, even yours, into a Viewbox to change its scale. That was my first approach, but it doesn't work. In fact, it seems any time you use Xaml expanded notation to set the Content property for a button it doesn't work.
<Style x:Key="SquareButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="SquareAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Square"/>
<Setter Property="Content">
<Setter.Value>
<!-- This square will never show -->
<Rectangle Fill="Blue" Width="20" Height="20" />
</Setter.Value>
</Setter>
I actually think this is a bug, but luckily there is a workaround.
Tim Heuer wrote an excellent article on the simplest way to use a Xaml Path as the artwork for a button. That article is here:
http://timheuer.com/blog/archive/2012/09/03/using-vectors-as-appbar-button-icons.aspx
In short, you need to define a style that sets up all the bindings correctly:
<Style x:Key="PathAppBarButtonStyle" BasedOn="{StaticResource AppBarButtonStyle}" TargetType="ButtonBase">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Path Width="20" Height="20"
Stretch="Uniform"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Data="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</DataTemplate>
</Setter.Value>
</Setter>
Then you create a style that inherits from that style and you paste in your path. Here is the style for your artwork you listed above:
<Style x:Key="CrossButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource PathAppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="CrossAppBarButton"/>
<Setter Property="AutomationProperties.Name" Value="Cross"/>
<Setter Property="Content" Value="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z"/>
</Style>
And finally, you use it in your AppBar like this:
<Button Style="{StaticResource CrossButtonStyle}" />
Dev support, design support and more awesome goodness on the way:
http://bit.ly/winappsupport
I'm pretty positive you can't just inject Path Data into an Image Source and expect it to magically work unless it's through a Drawing Object as Source. What you can however do is adopt your Path into a ContentControl for re-use in the same way without having to go through the trouble of Drawing objects for every instance.
So instead of;
<Image Source="..."/>
Just do something like this and plop it in your Object.Resources or ResourceDictionary;
<Style x:Key="YourThingy" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Path Fill="#FFCCCCCC" Stroke="#FF000000" StrokeThickness="1.34377062" StrokeMiterLimit="4" x:Name="path3007" Data="M372.58272,134.72445C167.96301,134.72445 2.06820310000001,300.58818 2.06820310000001,505.20789 2.06820310000001,709.8276 167.96301,875.72241 372.58272,875.72241 577.20243,875.72241 743.06616,709.8276 743.06616,505.20789 743.06616,300.58818 577.20243,134.72445 372.58272,134.72445z M280.73888,251.77484L455.94149,251.77484 455.94149,413.70594 628.16035,413.70594 628.16035,588.97071 455.94149,588.97071 455.94149,773.71514 280.73888,773.71514 280.73888,588.97071 106.22005,588.97071 106.22005,413.70594 280.73888,413.70594 280.73888,251.77484z" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then just plop it on your view wherever and as many times as you like;
<ContentControl Style="{StaticResource YourThingy}"/>
You will however want to play with that Path of yours. It seems set a large size, but hopefully this provides a good alternative for your circumstance. Cheers!
This is for a TextBox control on a login screen, where the TextBox contains the username. I want the TextBox to perform in the following way:
When the content is empty the content should be set to "Username".
When the TextBox is clicked I want the content to be set to
"" i.e; nothing (unless the content has already been edited by the user).
This is a pretty standard feature nowadays, something like this wordpress login (at the top of page). coudn't think of a better example than this I'm afraid :)
So, anyway, I've already done this using a ViewModel and it works well, but I'd like to know if this can be done purely from the XAML end. No business logic is concerned so I think it would be better to do it without the VM.
Find the below samples help you to find your way.
http://bendewey.wordpress.com/2008/09/27/wpf-shadowed-textbox-watermark/
http://www.c-sharpcorner.com/uploadfile/rahul4_saxena/watermark-textbox-in-wpf/
http://www.codeproject.com/Articles/26977/A-WatermarkTextBox-in-3-lines-of-XAML
Pure XAML:
<Grid>
<TextBox Width="250" VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="SearchTermTextBox" Margin="5"/>
<TextBlock IsHitTestVisible="False" Text="Enter Search Term Here" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0" Foreground="DarkGray">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=SearchTermTextBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
Taken from: https://stackoverflow.com/a/21672408/4423545
The Extended WPF Toolkit has a Watermark Textbox that will do just what you're asking in pure XAML. There are other libraries out there as well.
The good thing about using the Extended WPF Toolkit is you can pick it up on Nuget and install and install updates directly through Visual Studio.
I want to take the highlight background off of the radtreeview. I created a style to do this, but I keep getting errors and exceptions such as "Items collection must be empty." If I comment out the style the application works fine, so I know that it is the cause of the problem. I am fairly new to WPF, and I am sure I just don't understand how to use styles yet.
Thanks for your help. Here is the code.
<Grid x:Name="LayoutRoot" Background="Salmon">
<telerik:RadTreeView x:Name="radTreeView" Margin="8" ItemsSource="{Binding Errors}" Background="Salmon" Style="{StaticResource treeStyle}">
<Style TargetType="{x:Type telerik:RadTreeViewItem}" x:Name="treeStyle">
<Setter Property="Focusable" Value="False"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{x:Null}"/>
<Setter Property="BorderBrush" Value="{x:Null}"/>
</Trigger>
</Style.Triggers>
</Style>
<telerik:RadTreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}" >
<Grid Background="Salmon">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Description}" IsHitTestVisible="False" />
<ListBox Grid.Row="1" ItemsSource="{Binding Messages}" Margin="20,0,0,0" BorderBrush="#00000000" BorderThickness="0" Background="Salmon" IsHitTestVisible="False" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Message}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</HierarchicalDataTemplate>
</telerik:RadTreeView.ItemTemplate>
</telerik:RadTreeView>
</Grid>
</UserControl>
If you know that this is not going to work, I was also trying to get rid of the highlight with the style code:
<Style TargetType="TreeViewItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#FFF"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="#000"/>
</Style.Resources>
</Style>
You get exceptions because your style tag is actually an item in the tree, and you have ItemsSource set.
Surround the style with <telerik:RadTreeView.ItemContainerStyle> tag.
This should solve the exception but it will not give you the result that you expect since the control template of the tree view item is actually showing another border that is not affected by the Background property. You will need to change the control template.
Telerik change the styles between releases, so giving you a template of a wrong version will probably won't help you.
But, you can go to the installation folder for Telerik and look for a folder called "Themes". There you'll find a solution with all the themes for telerik.
Choose the one that you use.
Find the resource dictionary for the tree view and copy the style and template for the item to your project.
Change xmlns definitions, make sure you have all the brushes and resources that the style depends upon.
Run to see that the style is ok.
In the template, find the VisualState with x:Name="MouseOver" and delete the storyboard inside it.