Background image data binding from source not works - wpf

Inside my window xaml:
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Background">
<Setter.Value>
<VisualBrush>
<VisualBrush.Visual>
<Image Source="{Binding Path=ImageSource,Converter={StaticResource imgconverter}}">
<Image.BitmapEffect>
<BlurBitmapEffect KernelType="Box" />
</Image.BitmapEffect>
</Image>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Style>
</Grid.Style>
</Grid>
(I added to window resources this converter)
I would like to add background image with blur effect to this grid (with MVVM pattern) but my property never called in my viewmodel. If i use just converter with "Path=." the converter going to work but I have to use static ImageSource in conveter because if i add any object type for ImageSource (to path) (BitmapImage,ImageSource,..,etc), the converter will not call. (I tried to use UpdateSourceTrigger with PropertyChanged value but this solution didn't help to me.) The converter just an unwanted solution because this is the only one way to set my background correctly because my ImageSouce property has wanted value but its not works without converter and unfortunately the converter will not work too if i add any path to binding.
Here is my property inside the ViewModel:
private ImageSource _imageSource;
public ImageSource ImageSource
{
get
{
return _imageSource;
}
set
{
_imageSource = value;
OnPropertyChanged();
}
}
Any idea to set my background image with Blur effect correctly with MVVM pattern and without use uri path? (i dont want to save the image to physical storage)

A VisualBrush is not part of the element tree so it doesn't inherit the DataContext of the Grid.
But you could define the Image as a resource and bind its Source property to a property of the parent window using an {x:Reference}. This should work:
<Window x:Class="WpfApplication1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300" x:Name="win">
<Window.Resources>
<local:imgconverter x:Key="imgconverter" />
</Window.Resources>
<Grid>
<Grid.Resources>
<Image x:Key="img" Source="{Binding Path=DataContext.ImageSource,Converter={StaticResource imgconverter}, Source={x:Reference win}}">
<Image.BitmapEffect>
<BlurBitmapEffect KernelType="Box" />
</Image.BitmapEffect>
</Image>
</Grid.Resources>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Visual="{StaticResource img}" />
</Setter.Value>
</Setter>
</Style>
</Grid.Style>
<TextBlock Text="..." />
</Grid>
</Window>

Related

Can't set both ContentTemplateSelector and Template properties on a DataGridColumnHeader

In short, the question title says it all. For those that want more detail, here is the crux of my problem: I need to apply a custom ControlTemplate to the DataGridColumnHeader elements in my DataGrid control, but I also need to style them differently, depending on the cell data nearest the header. However, when I set both the ContentTemplateSelector and Template properties on a DataGridColumnHeader element, the DataTemplateSelector that is set as the value of the ContentTemplateSelector property is not called. Commenting out the Template property setting confirms this to be the case, as the DataTemplateSelector element will now be called.
Yes, I know that you guys love to see some code, but I have completely templated the whole DataGrid control to look like Excel, so as you can imagine, I have far too much code to display here. But just to please you code hungry devs, I've recreated my problem in a much simpler example... let's first see the XAML:
<Window x:Class="WpfApp1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Local="clr-namespace:WpfApp1"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.Items>
<System:String>One</System:String>
<System:String>Two</System:String>
<System:String>Three</System:String>
</DataGrid.Items>
<DataGrid.Resources>
<Local:StringDataTemplateSelector x:Key="StringDataTemplateSelector" />
<Style TargetType="{x:Type DataGridColumnHeader}" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="ContentTemplateSelector" Value="{StaticResource StringDataTemplateSelector}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" />
<Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
</DataGrid>
</Grid>
</Window>
Now the most simple DataTemplateSelector class:
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp1
{
public class StringDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
Debugger.Break();
return null;
}
}
}
In the XAML, we see a DataGrid, with just one DataGridTemplateColumn and three string values, one on each row, and some resources. There is a Style for the DataGridColumnHeader element in the Resource section, with the most simple ControlTemplate set up for it, that only includes the required named parts from the default ControlTemplate.
If you run the application as it is, then it will NOT currently break at the Debugger.Break() method in the StringDataTemplateSelector class. This is unexpected. If you now comment out the setting of the Template property in the Style and run the application again, then you will now see that program execution will now break at the Debugger.Break() method, as expected.
Further information:
In the Remarks section of the ContentControl.ContentTemplateSelector Property page of MSDN, it states that
If both the ContentTemplateSelector and the ContentTemplate properties are set, then this property is ignored.
However, it does not mention the Template property and there is also no mention of this on the Control.Template Property page on MSDN.
Furthermore, I tried this same setup using a simple Button control and can confirm that setting both the ContentTemplateSelector and the ContentTemplate properties on that does NOT stop the StringDataTemplateSelector class from being called:
<ItemsControl>
<ItemsControl.Resources>
<Local:StringDataTemplateSelector x:Key="StringDataTemplateSelector" />
<Style TargetType="{x:Type Button}">
<Setter Property="ContentTemplateSelector" Value="{StaticResource StringDataTemplateSelector}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse Stroke="Red" StrokeThickness="1" Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding Height}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Resources>
<Button Content="One" />
<Button Content="Two" />
<Button Content="Three" />
</ItemsControl>
So, what I'm after is a way to apply a custom ControlTemplate element to the DataGridColumnHeader objects, yet still be able to have the DataTemplateSelector class called during the rendering process.
add a content presenter in your controltemplate?
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" />
<Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" />
<ContentPresenter></ContentPresenter>
</Grid>
</ControlTemplate>

Change Content Template on Button Click Wpf

I have the following XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WpfApplication4" Height="300" Width="300">
<Window.Resources>
<ControlTemplate x:Key="simpleErrorTemplate">
<TextBox Margin="10,10,10,5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Text="T1" />
</ControlTemplate>
<ControlTemplate x:Key="detailedErrorTemplate">
<StackPanel>
<TextBox Margin="10,10,10,5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Text="T2" />
<TextBox Margin="10,10,10,5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Text="T3" />
<TextBox Margin="10,10,10,5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Text="T4" />
</StackPanel>
</ControlTemplate>
</Window.Resources>
<Grid>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Template"
Value="{StaticResource simpleErrorTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Button,Path=IsPressed}" Value="True">
<Setter Property="Template" Value="{StaticResource detailedErrorTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<Button x:Name="Button" Content="Button" Height="40" Width="129" Margin="88,5,76,5" Grid.Row="1" Click="Button_Click1"/>
</Grid>
</Window>
I had like to change the GUI style the moment I click the button just like it happends when DataTrigger is set for the Checkbox.
The code Behind looks as follows however the moment I click the button the window closes and no result.
import wpf
from System.Windows import Application, Window
class MyWindow(Window):
def __init__(self):
self.ui = wpf.LoadComponent(self, 'WpfApplication4.xaml')
def Button_Click1(self, sender, e):
self.ContentTemplate = Window.Resources.FindName(self,'detailedErrorTemplate')
if __name__ == '__main__':
Application().Run(MyWindow())
Why the template wont change?
Thank you.
I am unfamiliar with ironpython, but in your click handler it looks like you're setting the ContentTemplate of your window, which seems incorrect. I assume your detailedErrorTemplate is supposed to apply to your ContentControl. In this case you need to add x:Name to your ContentControl (i.e.: x:Name="TheContentControl") in order to reference the ContentControl in your code behind and set (in c#)
TheContentControl.Template = this.Resources["detailedErrorTemplate"] as ControlTemplate;
You have key's for your resources, not Names, so you need to index the resource dictionary by the key.
Edit details based on comments.
In your xaml, change your line
<ContentControl>
to
<ContentControl x:Name="TheContentControl">
Since we now have a name for the ContentControl, you can properly change the ControlTemplate of the ContentControl (NOT the window. I understand that your ContentControl effectively encompasses the Window, but you can't just assign that ControlTemplate meant for the ContentControl to the Window like that). Change your line of code from:
self.ContentTemplate = Window.Resources.FindName(self,'detailedErrorTemplate')
to (I hope I'm doing the syntax correctly).
self.TheContentControl.Template = self.Resources['detailedErrorTemplate']

Binding child DataContext from within DataTrigger

I have been trying to develop a bar of RadioButtons (styled as ToggleButtons) which select which content is displayed in a ContentControl. I have been able to use a DataTrigger to display the proper view in the ContentControl based on which RadioButton is checked, but I am also trying to bind a view model from the parent DataContext into the child's DataContext, without success. A minimum sample is as follows:
<Window x:Class="WpfApplication1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
<RadioButton Name="rbShowChild" Content="Show Child" Style="{StaticResource {x:Type ToggleButton}}" />
</StackPanel>
<ContentControl Grid.Row="1">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rbShowChild}" Value="True">
<Setter Property="Content">
<Setter.Value>
<local:ChildView DataContext="{Binding ChildViewModel, PresentationTraceSources.TraceLevel=High}"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</Window>
The Binding to the ChildViewModel seems to be the part that is not working as expected. For completeness, here is the related ChildView.
<UserControl x:Class="WpfApplication1.ChildView"
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:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<TextBlock Text="Child" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</Grid>
</UserControl>
And the ViewModels:
namespace WpfApplication1
{
public class ChildViewModel
{
public string Text { get; set; }
public ChildViewModel()
{
Text = "It works!";
}
}
public class MainWindowViewModel
{
public ChildViewModel ChildViewModel { get; set; }
public MainWindowViewModel()
{
ChildViewModel = new ChildViewModel();
}
}
}
And the MainWindow DataContext is set as follows:
var window = new MainWindow()
{
DataContext = new MainWindowViewModel()
};
window.Show();
The Output window (with PresentationTraceSources.TraceLevel=High) shows this:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:Path=ChildViewModel; DataItem=null; target element is 'ChildView' (Name=''); target property is 'DataContext' (type 'Object')
which leads me to think that somehow the MainWindow.DataContext is not the DataContext used to resolve the "{Binding ChildViewModel}" expression in the trigger, but I couldn't find any DataContext property on the trigger, nor have I been able to find anything in my searches to suggest an answer for this.
I would very much appreciate any suggestions to resolve this.
UPDATE: Looks like the issue is with how you are setting the data context.
Set the DataContext of the window in the XAML, then set the data context of the ContentControl to the ChildViewModel. Then set the actual displayed Content (AKA ChildView) Value. The ContentControl is the host of this view, so it should be the DataContext supplier. I myself am a little unsure the exact mechanics going on, but I know for fact the below code works.
NOTE Your XML namespaces may vary from the code below. Adjust accordingly.
<Window.DataContext>
<!-- Data Context added here OR in code-behind initialize-->
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
<RadioButton Name="rbShowChild" Content="Show Child" Style="{StaticResource {x:Type ToggleButton}}" />
</StackPanel>
<!-- Bind the ContentControls Data context-->
<ContentControl DataContext="{Binding ChildViewModel}" Grid.Row="1">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rbShowChild}" Value="True">
<Setter Property="Content">
<Setter.Value>
<!-- Just simply set the view content here, no binding.-->
<local:ChildView />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
Doing this, I have run your application just fine in a project.

Button control with an icon in it

I want to show an icon in some of my buttons. Since aligning an image inside a button isn't exactly trivial, I though a user control, derived control or something would come in handy. So I googled, tried, compiled and now I came up with the following code.
Unfortunately, my properties are not bound to anything and I cannot see the text nor an image. How can I make this work? What's the missing piece? I'm using VS2010 and .NET 4.0.
XAML:
<Button
x:Class="MyNS.IconButton"
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">
<Button.Template>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding IconSource}" Name="Icon" Width="{Binding IconSize}" Height="{Binding IconSize}" Margin="0,0,4,0"/>
<ContentPresenter/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsEnabled" Value="False">
<Setter Property="Image.Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Code file:
public partial class IconButton : Button
{
public static DependencyProperty IconSourceProperty = DependencyProperty.Register(
"IconSource",
typeof(ImageSource),
typeof(IconButton));
public static DependencyProperty IconSizeProperty = DependencyProperty.Register(
"IconSize",
typeof(int),
typeof(IconButton),
new PropertyMetadata(11));
public ImageSource IconSource
{
get { return (ImageSource) GetValue(IconSourceProperty); }
set { SetValue(IconSourceProperty, value); }
}
public int IconSize
{
get { return (int) GetValue(IconSizeProperty); }
set { SetValue(IconSizeProperty, value); }
}
public IconButton()
{
InitializeComponent();
}
}
It looks like your data binding might be the source of the issues. With those changes I was able to get an image to display in another page using your IconButton.
I made the following changes:
<!-- Added x:Name="_this" -->
<Button x:Class="ButtonTest.IconButton"
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="300" d:DesignWidth="300"
x:Name="_this">
<Button.Template>
<ControlTemplate>
<StackPanel Orientation="Horizontal"
Background="Red">
<!-- Bindings Changed Here -->
<Image Source="{Binding ElementName=_this, Path=IconSource}"
Name="Icon"
Width="{Binding ElementName=_this, Path=IconSize}"
Height="{Binding ElementName=_this, Path=IconSize}" Margin="0,0,4,0"/>
<ContentPresenter />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsEnabled" Value="False">
<Setter Property="Image.Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>

Parametrized Style within UserControl?

I used this MSDN tutorial to create an eye candy look for all the Button controls of my window, and that worked fine.
To make it even more reusable, I tried to put all in a UserControl: I created a ImageButton UC, then I encapsulated all that <Style> from <Window.Resources> to <UserControl.Resources>.
Then I changed my Button instances in XAML, from:
<Button Tag="Face.jpg" Content="Foo" />
To:
<uc:ImageButton Tag="Face.jpg" Content="Foo" />
And the style stopped being applied.
Here's the UC code:
<UserControl x:Class="GDTI.UI.Main.View.UserControls.ImageButton"
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="300" d:DesignWidth="300">
<UserControl.Resources>
<Style TargetType="Button">
<Setter Property="MaxWidth" Value="250" />
<Setter Property="Margin" Value="5" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" >
<Setter.Value>
<SolidColorBrush Color="Orange" Opacity="0.4" />
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate >
<StackPanel>
<Image Source="{Binding Tag,
RelativeSource={RelativeSource
FindAncestor,
AncestorType='Button'}}" />
<TextBlock Margin="10"
HorizontalAlignment="Center"
Text="{Binding Content,
RelativeSource={RelativeSource
FindAncestor,
AncestorType='Button'}}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button/>
What am I missing?
Thank you!
The bindings in the button-style target properties on the button, which no longer has the properties set. You need to forward those to the UserControl if you want to retain the style's integrity:
<!-- Inside UserControl declaration -->
<Button Content="{Binding Caption, RelativeSource={RelativeSource AncestorType=UserControl}}"
Tag="{Binding ImageSource, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
Where Caption and ImageSource should be new dependency properties defined on the UserControl (in code-behind).
Note that you can never bind to Content in a UserControl (hence the Caption property), here the Button itself is the Content of the UserControl.
Alternatively you could directly change the targeting in the style by changing the AncestorType to UserControl which bypasses the Button. Binding beyond the templated control is not exactly good practice but your are still inside the UserControl so it may be forgivable.
Either way this is a bit hacky and it might be better to inherit from Button instead.

Resources