Setting DataContext on DataGrid.RowStyle - c#

Using the following sample R# (resharper) is not able to find the datacontext of the Row style and warns about a wrong binding (at runtime works fine). Seems like the Style is not getting the DataContext of the ItemsSource:
MainWindow.xaml:
<Window x:Class="TestDatacontext.MainWindow"
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:testDatacontext="clr-namespace:TestDatacontext"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance testDatacontext:MainWindowVM}" >
<DataGrid ItemsSource="{Binding Items}" >
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" >
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
</Window>
MainWindowVM:
using System.Collections.ObjectModel;
namespace TestDatacontext
{
class MainWindowVM
{
public ObservableCollection<ItemVM> Items { get; private set; }
}
}
ItemVM:
namespace TestDatacontext
{
class ItemVM
{
public string Name { get; set; }
}
}

You are correct, ReSharper has no knowledge about how RowStyle will be used in this particular control (is it style per every item of ItemsSource? or some kind of header style and bindings will have access to ItemsSource object itself?), so it stops traversing tree looking for DataContext type on Style declaration.
This issue can be solved with additional annotation on Style declaration:
<Style TargetType="DataGridRow" d:DataContext="{d:DesignInstance vms:ItemVM}">
<Setter Property="Header" Value="{Binding Name}" />
</Style>
Project will compile fine, VS designer and R# will work, but VS xaml support will produce 1 error in errors window - "Property 'DataContext' is not attachable to elements of type 'Style'". That's a bit annoying, but works. Other way is to quilify property type like this:
<Style TargetType="DataGridRow">
<Setter Property="Header" Value="{Binding (vms:ItemVM.Name)}" />
</Style>
But it produces VS xaml support error too :) and have slightly different behavior in runtime - this binding will work only with Name property of ItemVM type and will not work if somehow VM object will be replaced with some other object of different type with Name property at runtime (so binding became "strongly-typed").
We are still looking for a better way to solve this kind of problems in ReSharper 8.0 and make VS designer happy, sorry for confusing!

Related

ItemContainerStyle cleared by loading custom style

So, I have this issue with making a custom treeview with custom treeviewitem:s where the ItemContainerStyle gets cleared by loading the style from the custom style.
It work like this. I have custom MyTreeViewItem based on TreeViewItem.
<TreeViewItem x:Class="UI.MyTreeViewItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<TreeViewItem.Resources>
<Style x:Key="MyTreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="Background" Value="#AEFFC1" />
</Style>
</TreeViewItem.Resources>
</TreeViewItem>
As you can see I have just have a simple coloring here just to make sure that the styling it self works. This how ever wont load unless I do like this in code behind.
EDIT: I know things like coloring don't need to be put here as there was intended to be a template here instead. How ever since noting really worked, I just stripped this down to the bones to make sure I put something super simple in that I know should work in case it was becaouse of the template it self.
public partial class MyTreeViewItem : TreeViewItem
{
public MyTreeViewItem()
{
InitializeComponent();
this.Loaded += MyTreeViewItem_Loaded;
}
private void MyTreeViewItem_Loaded(object sender, RoutedEventArgs e)
{
this.Style = Resources["MyTreeViewItemStyle"] as Style;
}
}
This works grate. Have used this several other times with other controls to have custom styling for controls needed to be loaded up with out having to bother to "restyle" everything over and over again.
How ever there are a issue I come across with this. And that is when ItemContainerStyle is being used of this kind of custom styled controller.
<local:BaseTreeView x:Class="My.Navigator.NavigatorTreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ui="clr-namespace:UI;assembly=BaseCode"
d:DesignHeight="450" d:DesignWidth="800">
<ui:MyTreeView ItemsSource="{Binding Path=Nodes}">
...
<ui:MyTreeView.ItemContainerStyle>
<Style TargetType="{x:Type ui:MyTreeViewItem}">
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<EventSetter Event="Selected" Handler="TreeView_SelectedItemChanged" />
<EventSetter Event="Expanded" Handler="TreeView_NodeExpanded" />
<EventSetter Event="Collapsed" Handler="TreeView_NodeCollapsed" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</ui:MyTreeView.ItemContainerStyle>
</ui:MyTreeView>
</local:BaseTreeView>
This ui:MyTreeView.ItemContainerStyle as you see above gets totally ignored once the style is loaded, by this.Style = Resources["MyTreeViewItemStyle"] as Style; in the MyTreeViewItem_Loaded.
That means these Setters, EventSetters and Triggers will not fire at all, as they still are needed to be able to be added as as additional rules.
How can this be solved, so that the predefined styling in the custom control can be loaded and by using this control, you can still can hook up unique rules, like above with out having the the predefined overrule them?
It's not really clear why you are doing what you are doing. All I can say is that you are overwriting the Style value by assigning it explicitly after the control was initialized with the value from the TreeView.ItemContainerStyle.
Normally, on a UserControl, you would set the properties locally on the element:
<TreeViewItem x:Class="UI.MyTreeViewItem"
...
d:DesignHeight="450" d:DesignWidth="800"
Background="#AEFFC1">
</TreeViewItem>
or in code-behind:
private void MyTreeViewItem_Loaded(object sender, RoutedEventArgs e)
{
this.Background =
new SolidColorBrush(ColorConverter.ConvertFromString("#AEFFC1"));
}
When writing a custom Control, you would provide a default Style in Generic.xaml. This is the best solution as it allows the control to be styled (where custom styles are allowed to override default values provided by the default Style). External styles are merged implicitly. You should prefer a custom Control over a UserControl.
Your current code does not allow styling as you forcefully override values provided by a custom Style:
// Overwrite previous property value.
this.Style = someValue;
This is programming 101, first grade: an assignment always overwrites the old value (reference) of the variable.
Assumming that you are knowing what you are doing and you don't want to use one of the above solution, you must manually merge both styles using the Style.BasedOn property:
private void MyTreeViewItem_Loaded(object sender, RoutedEventArgs e)
{
var defaultStyle = Resources["MyTreeViewItemStyle"] as Style;
defaultStyle.BasedOn = this.ItemContainerStyle;
this.Style = defaultStyle;
}
See: Control authoring overview: Models for Control Authoring

WPF ComboBox truncating items

I found a strange behavior when using ComboBox with enum items. I noticed that the popup that displays the entries when I click on the ComboBox truncates long items. I figured out that this happens because I define a TextBlock style with a fixed Width. What is strange is that, the Width only affects the ComboBox when I use enum items. It does not happen If I use string ones instead.
Here's a picture with what's going on. The third item should be "VeryLongTypeName".
Here is the code sample written according with the MVVM pattern.
The UserControl XAML:
<UserControl.DataContext>
<local:SampleViewModel/>
</UserControl.DataContext>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Width" Value="70"/>
<Setter Property="Margin" Value="0,0,5,0"/>
</Style>
</StackPanel.Resources>
<DockPanel>
<TextBlock Text="Items"/>
<ComboBox ItemsSource="{Binding ItemsList}" SelectedItem="{Binding Item}"/>
</DockPanel>
<DockPanel>
<TextBlock Text="String Items"/>
<ComboBox ItemsSource="{Binding StringItemsList}" SelectedItem="{Binding StringItem}"/>
</DockPanel>
</StackPanel>
The SampleViewModel code:
public class SampleViewModel
{
public enum SomeType { Type1, Type2, VeryLongTypeName };
public IEnumerable<SomeType> ItemsList
{
get { return (SomeType[])Enum.GetValues(typeof(SomeType)); }
}
public SomeType Item { get { return ItemsList.First(); } set { } }
public IEnumerable<string> StringItemsList
{
get { return ItemsList.Select(type => type.ToString()); }
}
public string StringItem { get { return StringItemsList.First(); } set { } }
}
If you build the code sample, in the second ComboBox below the one from the picture, things go smoothly with string values.
I have the following questions:
Why does changing the type affect the graphics?
How do I fix the ComboBox display when using enum?
Any help is welcomed.
Your textblock style is for all textblocks. The content of the combobox is also displayed with textblocks and you limited the width of textblocks to 70.
Use a key for your style or set another textblock style for the comboboxes.
The problem also happens when listing items with a ListBox. I used Live Property Explorer to see what's going on. Both cases render the content in a TextBlock, but only when using enum values the style defined as resource is applied. Don't know why this happens, but that's how it is.
To fix the problem for enum and possibly other types except string, I added the following style, based on #Mardukar's idea:
<Style TargetType="ComboBoxItem">
<Style.Resources>
<Style TargetType="TextBlock" BasedOn="{x:Null}"/>
</Style.Resources>
</Style>
#Fredrik's idea of changing the ComboBox.ItemTemplate also works.
For ListBox, the style needs to have TargetType either ListBoxItem or ListBox.

Databinding not working on Avalondock window

I have a project with a Window Manager using AvalonDock.
Basically there is two Element : a LayoutAnchorableItem to show my different tool box (currently one, consisting of a Treeview) and a LayoutItem to show the document opened with the treeview (a custom control, with bindable parameters - in theory)
The ViewModel of the DockingManager hosts the ObservableCollection named Panes that will be the LayoutItems.
Things works "fine" if I don't try to bind the parameters in the XAML, and force the values like this
<avalonDock:DockingManager.LayoutItemTemplateSelector>
<panes:PanesTemplateSelector>
<panes:PanesTemplateSelector.ExchangeViewTemplate>
<DataTemplate>
<xchng:Exchange/>
</DataTemplate>
</panes:PanesTemplateSelector.ExchangeViewTemplate>
<panes:PanesTemplateSelector.GraphViewTemplate>
<DataTemplate>
<grph:Graph TickerCode="ILD" ExchangeCode="EPA"/>
</DataTemplate>
</panes:PanesTemplateSelector.GraphViewTemplate>
</panes:PanesTemplateSelector>
</avalonDock:DockingManager.LayoutItemTemplateSelector>
Exchange is the toolbox and Graph is the LayoutItems.
The initial databinding for the docking manager is done like this :
<avalonDock:DockingManager Margin="0,0,0,0"
Grid.Row="1"
AnchorablesSource="{Binding Tools}"
DocumentsSource="{Binding Panes}"
ActiveContent="{Binding ActiveDocument, Mode=TwoWay, Converter={StaticResource ActiveDocumentConverter}}"
x:Name="dockManager">
Note that Pane is of type GraphViewModel which has two public parameters : ExchangeCode and TickerCode.
The thing is I want to bind the TickerCode and ExchangeCode to the Panes.TickerCode and Panes.ExchangeCode values.
So I tried this :
<grph:Graph TickerCode="{Binding TickerCode, UpdateSourceTrigger=PropertyChanged}" ExchangeCode="{Binding ExchangeCode, UpdateSourceTrigger=PropertyChanged}"/>
But it does nothing : TickerCode and ExchangeCode in the custom control are equal to "" contrary to when I force the values in the XAML.
Also the somewhat weird thing is that if I step in the code execution, Panes actually have values for TickerCode and ExchangeCode, they just don't bind. For instance, the code that actually create the pane is
public void AddGraph(string FullName, string ExchangeCode, string TickerCode)
{
var graphViewModel = new GraphViewModel(FullName, ExchangeCode, TickerCode);
_panes.Add(graphViewModel);
ActiveDocument = graphViewModel;
}
Here, every step has both values. And let's imagine that I add 5 different panes, they are all with their correct ExchangeCode and TickerCode, but nothing is passed to the custom control.
If you need more info on my custom control that values are bound to, here is the code : Passing parameters to custom control (databinding).
Remark: As you see I didn't put much of my code, make request if you think it may help and I will add what's needed. Note that the global logic of the whole window manager is the same provided in the AvalonDock test app (AvalonDock.MVVMTestApp).
For example, if I’ve got ChartView and ChartViewModel:
In MainWindow.xaml:
<xcad:DockingManager x:Name="dockingManager"
AnchorablesSource="{Binding Path=Anchorables}"
DocumentsSource="{Binding Path=Documents}"
ActiveContent="{Binding Path=ActiveDocument, Mode=TwoWay, Converter={StaticResource ActiveDocumentConverter}}">
<xcad:DockingManager.LayoutItemTemplateSelector>
<selfViewPane:PaneTemplateSelector>
<selfViewPane:PaneTemplateSelector.ChartViewTemplate>
<DataTemplate>
<selfViewDocument:ChartView />
</DataTemplate>
</selfViewPane:PaneTemplateSelector.ChartViewTemplate>
</selfViewPane:PaneTemplateSelector>
</xcad:DockingManager.LayoutItemTemplateSelector>
<xcad:DockingManager.LayoutItemContainerStyleSelector>
<selfViewPane:PaneStyleSelector>
<selfViewPane:PaneStyleSelector.ChartViewStyle>
<Style TargetType="{x:Type xcad:LayoutItem}">
<Setter Property="Title" Value="{Binding Model.Title}"/>
<Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}"/>
<Setter Property="IconSource" Value="{Binding Model.IconSource}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
</Style>
</selfViewPane:PaneStyleSelector.ChartViewStyle>
</selfViewPane:PaneStyleSelector>
</xcad:DockingManager.LayoutItemContainerStyleSelector>
<xcad:DockingManager.LayoutUpdateStrategy>
<selfViewPane:LayoutInitializer />
</xcad:DockingManager.LayoutUpdateStrategy>
<xcad:LayoutRoot>
<xcad:LayoutPanel Orientation="Horizontal">
<xcad:LayoutAnchorablePane Name="ToolsPane" DockWidth="200">
</xcad:LayoutAnchorablePane>
<xcad:LayoutDocumentPane />
</xcad:LayoutPanel>
</xcad:LayoutRoot>
</xcad:DockingManager>
And:
In ChartViewModel I’ve got property ChartPlotModel:
/// <summary>
/// Gets or sets the ChartPlotModel.
/// </summary>
public PlotModel ChartPlotModel
{
get
{
return this.chartPlotModel;
}
set
{
if (this.chartPlotModel != value)
{
this.chartPlotModel = value;
this.RaisePropertyChanged("ChartPlotModel");
}
}
}
In ChartView I can bind:
<UserControl x:Class="Jofta.Analyzer.UI.Classes.View.Document.ChartView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:oxy="http://oxyplot.org/wpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<xctk:BusyIndicator IsBusy="{Binding Path=IsBusy}">
<Grid>
<oxy:PlotView Model="{Binding ChartPlotModel}" />
</Grid>
</xctk:BusyIndicator>
</UserControl>
In this example I’m binding to PlotView from oxyplot, but I think, you can use this pattern. You’ve got GraphViewModel, GraphView and TickerCode and ExchangeCode.

Does XAML have a conditional compiler directive for debug mode?

I need something like this for styles in XAML :
<Application.Resources>
#if DEBUG
<Style TargetType="{x:Type ToolTip}">
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FlowDirection" Value="LeftToRight"/>
</Style>
#else
<Style TargetType="{x:Type ToolTip}">
<Setter Property="FontFamily" Value="Tahoma"/>
<Setter Property="FlowDirection" Value="RightToLeft"/>
</Style>
#endif
</Application.Resources>
I recently had to do this and was suprised at how simple it was when I couldn't easily find any clear examples. What I did was add the following to AssemblyInfo.cs:
#if DEBUG
[assembly: XmlnsDefinition( "debug-mode", "Namespace" )]
#endif
Then, use the markup-compatability namespace's AlternateContent tag to choose your content based on the presense of that namespace definition:
<Window x:Class="Namespace.Class"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="debug-mode"
Width="400" Height="400">
...
<mc:AlternateContent>
<mc:Choice Requires="d">
<Style TargetType="{x:Type ToolTip}">
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FlowDirection" Value="LeftToRight"/>
</Style>
</mc:Choice>
<mc:Fallback>
<Style TargetType="{x:Type ToolTip}">
<Setter Property="FontFamily" Value="Tahoma"/>
<Setter Property="FlowDirection" Value="RightToLeft"/>
</Style>
</mc:Fallback>
</mc:AlternateContent>
...
</Window>
Now, when DEBUG is defined, "debug-mode" will also be defined, and the "d" namespace will be present. This makes the AlternateContent tag choose the first block of code. If DEBUG is not defined, the Fallback block of code will be used.
This sample code was not tested, but it's basically the same thing that I'm using in my current project to conditionally show some debug buttons.
I did see a blog post with some example code that relied on the "Ignorable" tag, but that seemed a lot less clear and easy to use as this method.
This is not possible in WPF/Silverlight/WP7.
On an interesting note, the standards document, ISO/IEC 29500 (Office Open XML File Formats), covers how this should be handled in an XML document, and XAML does support one of the items from that spec mc:Ignorable which allows us to do things like this:
<Page xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:c="Comments"
mc:Ignorable="c">
<Button Content="Some Text"
c:Content="Some other text" />
</Page>
to comment out attributes.
The XAML parser team (SL4, WP7.1, WPF) chose to use that spec to solve their needs for ignoring attributes, rather than just making something up. That is why some of the default XAML pages have the 'mc' namespace defined. I do think it would be cool if XAML one day supported the rest of the spec that allows the loading of alternate content.
The mc:Ignorable attribute is used by Blend to support design time functionality.
You could use a template selector. The DataTemplateSelector class is something you code. With the template selection method that you override, you could put your preprocessor directives.
http://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector.aspx
I wasn't happy with any of these and did this:
In my XAML I put any attributes or tags with a space just so i know I am screwing with them in the .cs file.
<Window x:Class="...
mc:Ignorable="d"
Title=""
BorderThickness="2"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
Height="200"
Width="500"
WindowStyle="None"
>
then in my code behind I do this:
public partial class ScanProgressWindow : Window
{
public ScanProgressWindow()
{
InitializeComponent();
#if DEBUG
this.WindowStyle = WindowStyle.SingleBorderWindow;
#endif
}
}
Works for me.
I feel like the given answers aren't the easiest to use. Here is my solution using a custom attachable dependency property:
using namespace Utility{
public static class DebugVisibility
{
public static readonly DependencyProperty IsVisibleProperty = DependencyProperty.RegisterAttached(
"Debug", typeof(bool?), typeof(DebugVisibility), new PropertyMetadata(default(bool?), IsVisibleChangedCallback));
private static void IsVisibleChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fe = d as FrameworkElement;
if (fe == null)
return;
#if DEBUG
fe.Visibility = Visibility.Visible;
#else
fe.Visibility = Visibility.Collapsed;
#endif
}
public static void SetIsVisible(DependencyObject element, bool? value)
{
element.SetValue(IsVisibleProperty, value);
}
public static bool? GetIsVisible(DependencyObject element)
{
return (bool?)element.GetValue(IsVisibleProperty);
}
}
}
and the xaml would be used like this:
<window ... xmlns:Util="clr-namespace:MyNamespace.Utility" >
<Label Util:DebugVisibility.IsVisible="True">
</window>
I kept it as a bool in case you wanted to add some other visibility logic in there. This is a nice simple toggle that can be bound to and attached to any control

How to make a WPF style inheritable to derived classes?

In our WPF app we have a global style with TargetType={x:Type ContextMenu}. I have created a MyContextMenu that derives from ContextMenu, but now the default style does not apply.
How can I tell WPF that I want MyContextMenu to inherit the default style from ContextMenu? Hopefully I can do this from within my control itself (via static ctor metadata override or something?) and not have to mess around in any xaml.
If you have a Style defined in your application like so:
<Style TargetType="{x:Type ContextMenu}" ...
Then that is an implicit Style, not a default Style. Default Styles are generally located in the same assembly as the control or in matching assemblies (i.e. MyAssembly.Aero.dll).
Implicit Styles are not automatically applied to derived types, which is probably what you are seeing.
You can either define a second Style, like so:
<Style x:Key="{x:Type ContextMenu}" TargetType="{x:Type ContextMenu}" ...
<Style TargetType="{x:Type local:MyContextMenu}" BasedOn="{StaticResource {x:Type ContextMenu}}" ...
Or you can leverage the Style property of your control. You could do the following from XAML
<local:MyContextMenu Style="{DynamicResource {x:Type ContextMenu}}" ...
or you can do this in your MyContextMenu like so:
public MyContextMenu() {
this.SetResourceReference(StyleProperty, typeof(ContextMenu));
}
In complement to CodeNaked's excellent suggestions, I tried specifying Style in the XAML part of MyContextMenu:
<ContextMenu x:Class=LocalProject.MyContextMenu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:AdelSoft_WS_FRA_Test.Composants"
mc:Ignorable="d"
Style="{DynamicResource {x:Type ContextMenu}}">
The compiler warned me that it cannot resolve the resource, but at runtime it looks quite able to.
Naturally, you can also use
Style="{StaticResource ContextMenuStyleName}">
if you use style names.

Categories