Maybe title is confusing you, but the problem is concretely here : I have XAML file which there is ControlTemplate for MyType (inherits from ContentControl). And in that Template i set its DataContext like this :
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
And when i want to get DataContext from MyType it shows null for first time. After clicking on it ( it is rectangle in window ) it changes to object what i need. I searched all things, all events what i am doing in LeftButtonDown there is no ApplyTemplate(),UpdateLayout() methods. Even i call these methods it doesn't help. What need to do ? P.S this DataContext is Parent of this item, i can send is a parameter but there must be other solution
this is part of XAML which Template located:
<Style TargetType="{x:Type s:Connector}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type s:Connector}">
<Grid Name="grid" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<Border BorderBrush="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
Path=BorderBrush}" x:Name="border" BorderThickness="2" Background="Transparent">
<Image Source="/DiagramDesigner;component/Resources/1337238611_port.png"/>
</Border>
<Rectangle Fill="Transparent" Margin="-2"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And this is where i want to get DataContext:
public DesignerItem ParentDesignerItem //
{
get
{
if (parentDesignerItem == null)
{
parentDesignerItem = this.DataContext as DesignerItem;
// if (parentDesignerItem==null) parentDesignerItem = (this.Template.FindName("grid", this) as Grid).DataContext as DesignerItem;
}
return parentDesignerItem;
}
set
{
parentDesignerItem = value;
}
}
I must say that this Style is in XAML which Style of the DesignerItem also.
The problem is in my algorithm P.S i restore some information from SQL, and add 2 times more Connectors with null parameters and when i want to access it gives me these null objects.
Related
I have a class assembly that contains a number of user controls. I want to add a new type of button to this class assembly and I also want to add styling to it. Specifically I want a button that contains multiple images for use in Normal, Hover (IsMouseOver) and disabled states. It will also contain text to display. Then I could add this in any application using this class assembly as follows:
<ns:ImageTextButton NormalImage="{StaticResource SomeImage}" HoverImage="{StaticResource SomeHoverImage}" Text={StaticResource SomeText}" />
First I created a C# class:
public class ImageTextButton : Button {
public DependencyProperty ImageProperty = DependencyProperty.Register(nameof(Image), typeof(DrawingBrush), typeof(ImageTextButton));
public DependencyProperty HoverImageProperty = DependencyProperty.Register(nameof(HoverImage), typeof(DrawingBrush), typeof(ImageTextButton));
public DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(ImageTextButton));
public DrawingBrush Image {
get { return GetValue(ImageProperty) as DrawingBrush; }
set { SetValue(ImageProperty, value); }
}
public DrawingBrush HoverImage {
get { return GetValue(HoverImageProperty) as DrawingBrush; }
set { SetValue(HoverImageProperty, value); }
}
public string Text {
get { return GetValue(TextProperty) as string; }
set { SetValue(TextProperty, value); }
}
}
Then I created a style in a Styles.xaml file which is compiled as a Resource.
<Style TargetType="{x:Type local:ImageTextButton}" x:Key="ImageTextButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="0" Background="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=Image}">
<StackPanel>
<Canvas x:Name="canvas" Background="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=Image}" />
<TextBlock Style="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=TextStyle}"
Text="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=Text}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=HoverImage}" TargetName="canvas" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have not been able to test this style and there very well may be issues in it. I can't get the application to start because the TargetType above doesn't exist. I believe this is because my Styles.xaml is a resource and the actual class is compiled. The error I get is Failed to create a 'Type' from the text 'local:ImageTextButton'.
How can I achieve this? Secondly, is there any way to apply this style by default to this type? I don't want to always have to specify Style={StaticResource ImageTextButtonStyle} in each instance of this user item.
First you have to decide if you are going to build an UserControl or a CustomControl.
UserControl needs to be derived from "UserControl" Base class. As your C# code extends "Button", that implementation doesn't fall under "UserControl".
For a CustomControl, your Style should be present in a file named "Generic.xaml" which SHOULD BE PLACED in a folder "Themes". (You can still change the default Themes Folder location). And your C# file should define the Key which will be used to find/target/identify the Xaml Style.
Your above setup should not work as it satisfies neither Usercontrol nor Custom Control Requirements.
I cannot create a full example but can direct you to a certain extent.
You need to have the below Static Method to set your default style as a bare minimum. Add remaining properties as required.
public class ImageButton : Button
{
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
public ImageButton()
{
}
For the Xaml Part. Create a Folder Themes and inside that create a resource dictionary by name "Generic.xaml". Inside your Generic.Xaml, add your styles. (Place your xaml logic inside the control template).
<Style TargetType="{x:Type bc:ImageButton}">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type bc:ImageButton}">
</ControlTemplate>
</Setter>
</Style>
All the above are bare minimum requirement for a CUSTOM CONTROL. In case you wish to create a UserControl, you need a .xaml & a .xaml.cs files. You can easily use visual studio context menu (rightclick on solution) and create a usercontrol.
Important: When you are creating a custom control, you dont need something like below
Background="{Binding RelativeSource={RelativeSource AncestorType=local:ImageTextButton}, Path=Image}">
Just use, TemplateBinding
Background="{TemplateBinding Image}">
The RelativeSource Mode FindAncestor is missing
change {RelativeSource AncestorType=local:ImageTextButton} to {RelativeSource FindAncestor, AncestorType=local:ImageTextButton}
I'm trying to create a control that uses a RevealBorderBrush as its border brush in XAML. I want to use the correct TargetTheme value for the brush, so I'm trying to bind to my application's current ActualTheme value. I'm using a templated control to do this. My C# code behind file is just the empty constructor that inherits from Control and sets the default style key. The following is my Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Plank">
<Style TargetType="local:PlankPL" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:PlankPL">
<Border
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}">
<Border.BorderBrush>
<RevealBorderBrush TargetTheme="{Binding Source=local:App, Path=ActualTheme}"/>
</Border.BorderBrush>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I'm pretty sure the binding statement is incorrect, but I'm not sure how to write it.
I haven't tested this but it should work. Name your control then use the below code, replace ControlTemplateName with what you used.
You can’t bind background data in ResourceDictionary, it doesn’t work.
It is recommended to write a UserControl and bind the RevealBorderBrush property of Border like following.
MyUserControl1.xaml:
<UserControl..>
<StackPanel>
<Border BorderThickness="3">
<Border.BorderBrush>
<RevealBorderBrush TargetTheme="{x:Bind theme1}"/>
</Border.BorderBrush>
</Border>
<TextBox Text = "text demo text "/>
</StackPanel>
</UserControl>
MyUserControl1.xaml.cs:
public sealed partial class MyUserControl1 : UserControl
{
private ApplicationTheme theme1;
public MyUserControl1()
{
this.InitializeComponent();
theme1 = Application.Current.RequestedTheme;
}
}
I'm working with WPF and I want to use attached properties to work with some styling things in the validation of the controls (my example of the problem is really simple, binding a simple text).
This is my attached property:
public class ToolTipExtension
{
public static readonly DependencyProperty ShowToolTipProperty = DependencyProperty.RegisterAttached(
"ShowToolTip", typeof(string), typeof(ToolTipExtension), new PropertyMetadata("Deffault"));
public static void SetShowToolTip(DependencyObject element, string value)
{
element.SetValue(ShowToolTipProperty, value);
}
public static string GetShowToolTip(DependencyObject element)
{
return (string) element.GetValue(ShowToolTipProperty);
}
}
I have a simple style dictionary like this
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:at="clr-namespace:CarpetaTecnicaWPF.AttachedProperties"
>
<Style TargetType="{x:Type TextBox}" x:Key="Blah" >
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(at:ToolTipExtension.ShowToolTip)}" FontSize="50"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
As you can see, I'm trying to bind the Text property to my attached property.
In my Page, I'm using the style like this:
<TextBox Style="{StaticResource Blah}" at:ToolTipExtension.ShowToolTip="Prueba?"/>
The thing is, the value Prueba? does not appear. When I inspect the tree, I see this:
But in runtime, the result of the binding is Deffault
What am I doing wrong?
Your binding is incorrect.
The TemplatedParent in this case is not what you actually need. The ControlTemplate for the error is not applied to the text box itself, it's a stand-alone control template. So you are just getting a default value from a wrong FrameworkElement.
To access the text box your error template is applied to, you need to use the AdornedElementPlaceholder in your ControlTemplate. From that AdornedElementPlaceholder, you can access your text box via the AdornedElement property.
Here is an example:
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<AdornedElementPlaceholder x:Name="adorner"/>
<TextBlock Text="{Binding ElementName=adorner, Path=AdornedElement.(at:ToolTipExtension.ShowToolTip)}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
So I have a ResourceDictionary to define my custom Window style. What I am struggling to do is to access controls from XAML file.
The ResourceDictionary looks like this
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="MyCustomWindowStyle" TargetType="{x:Type Window}">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="30"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<!-- the root window -->
<Border BorderThickness="0.3" BorderBrush="{DynamicResource GeneralDarkBlue}">
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
</Border>
<DockPanel Height="30" Background="{TemplateBinding Background}" VerticalAlignment="Top" LastChildFill="False">
<Viewbox x:Name="HamburgerMenu" DockPanel.Dock="Left" WindowChrome.IsHitTestVisibleInChrome="True">
<Viewbox.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{Binding SettingsClick}"/>
</Viewbox.InputBindings>
<Border Width="47" Height="32" Background="Transparent">
<Canvas>
<Path x:Name="TopbarIconHamburgerMenu" Margin="14,10" Data="M12.5,19h19.2v1H12.5V19z M12.5,13.7h19.2v1H12.5V13.7z M12.5,8.5h19.2v1H12.5V8.5z" Stretch="UniformToFill" Fill="#FFFFFF"/>
</Canvas>
</Border>
</Viewbox>
// the rest of viewboxes for minimize, maximize controls...
</DockPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And let's say I want to access the HamburgerMenu, so I do something like this
public partial class MyCustomWindowStyle : ResourceDictionary
{
public MyCustomWindowStyle()
{
InitializeComponent();
}
public void DoSomething()
{
var window = (Style)Application.Current.Resources["MyCustomWindowStyle"];
var hm = (Viewbox)window.Resources.FindName("HamburgerMenu");
}
}
and this returns null in the hm!
Any idea how to do this?
First of all, Style.Resource is a ResourceDictionary, and there are two important things to notice in the ResourceDictionary.FindName method documentation:
Summary section saying:
Not supported by this Dictionary implementation.
and Return Value section saying:
Always returns null.
Second of all, even if you tried to retrieve the ViewBox by key, it would have to be defined as a resource:
<Style x:Key="MyCustomWindowStyle" TargetType="{x:Type Window}">
<Style.Resources>
<ViewBox x:Key="HamburgerMenu" />
</Style.Resources>
</Style>
And it is not. It is a part of ControlTemplate's visual tree.
Third of all, ControlTemplate does not contain actual elements, but rather a recipe for creating them. So there's no actual ViewBox living inside the ControlTemplate to retrieve. Notice that ControlTemplate.FindName takes an additional parameter specifying an element for which the template was realized.
However, ControlTemplate does have a LoadContent method, which basically loads the visual tree defined by that template, and I think you could use it, and then invoke FindName on the root element. To simplify retrieval of the ControlTemplate let's first make it a resource:
<Style x:Key="MyCustomWindowStyle" TargetType="{x:Type Window}">
<Style.Resources>
<ControlTemplate x:Key="Template" TargetType="{x:Type Window}">
<Grid>
(...)
<ViewBox x:Key="HamburgerMenu" />
(...)
</Grid>
</ControlTemplate>
</Style.Resources>
<Setter Property="Template" Value="{StaticResource Template}" />
</Style>
Then this should do the trick for you:
var window = (Style)Application.Current.Resources["MyCustomWindowStyle"];
var template = (ControlTemplate)window.Resources["Template"];
var root = (FrameworkElement)template.LoadContent();
var hm = (ViewBox)root.FindName("HamburgerMenu");
Update
If your goal is to get hold of the ViewBox in an existing window with that template applied, first you need to know how to get hold of that particular window. It could be the Application.Current.MainWindow, otherwise you're highly likely to find it in the Application.Current.Windows collection. You could also implement the singleton pattern for that window, or use other methods like exposing a static property with reference to that window somewhere in your application, or using third-party tools, such as Service Locator in Prism.
Once you have the window in your hand, you only need to use the previously mentioned ControlTemplate.FindName method:
var window = (...);
var hm = (ViewBox)window.Template.FindName(name: "HamburgerMenu", templatedParent: window);
Note that accessing the resource dictionary in which the template was defined is not necessary.
As for why your attempts with previous solution failed - that's because ControlTemplate.LoadContent method yields freshly created element each time it is invoked, and modifying it does not reflect on elements previously created by that template.
OK, I've searched everywhere and in every single link I go to my problem is explained with xaml code.
I want to change the active tab's background and foreground (not its content, but the upper part which you select in order to make active) in a WPF project, but I'm looking for the C# code. The code below doesn't work for me:
if (tabs[0].IsEnabled) tabs[0].Background = Brushes.Blue;
else tabs[0].Background = Brushes.Black;
Do it in XAML if you use WPF.
You can bind to the TabControl's property ItemsSource. Than just define a Styletrigger to change the Background
OK, thanks to Venson I've finally got it and just in case someone wants to know how it works:
<TabControl ItemsSource="{Binding tabs}" Height="68" HorizontalAlignment="Left" Margin="156,23,0,0" Name="tabControl1" VerticalAlignment="Top" Width="268">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Margin="0,0,-4,0"
Background="Black"
BorderBrush="Blue"
BorderThickness="1,1,1,1"
CornerRadius="2,12,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Blue"></Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Margin="0,0,-4,0"
Background="Green"
BorderBrush="Blue"
BorderThickness="1,1,1,1"
CornerRadius="2,12,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
This code goes in the <Grid>of the <Window> tags of the MainWindow.xaml and
public MainWindow()
{
testClass testObject = new testClass();
testObject.tabs = new List<TabItem>();
testObject.tabs.Add(new TabItem());
testObject.tabs.Add(new TabItem());
testObject.tabs[0].Header = "NO WAY";
testObject.tabs[1].Header = "ON WAY";
testObject.tabs[0].Content = "WHAT";
testObject.tabs[1].Content = "HELL";
InitializeComponent();
this.DataContext = testObject ;
}
class testClass
{
public List<TabItem> tabs { set; get; }
}
this goes into the MainWindow.xaml.cs file.
Please note: the colors are only for the test, don't judge me for the bad contrast chosen!
I don't know if it's possible not to use another class, though..
foreach(var tab in tabs)
{
tab.Background = tab.IsEnabled ? Brushes.Blue :Brushes.Black;
}
But you can handle state of tabcontrol on active tab changes and set backgrounds of deactivated and activated tabs.