WPF Ribbon has poor image quality. I added
<Window.Resources>
<Style TargetType="{x:Type Image}">
<Setter Property="RenderOptions.BitmapScalingMode" Value="HighQuality" />
</Style>
</Window.Resources>
in my ribbon windows - but it doesnt helped. Also tried with
<ribbon:RibbonWindow.Resources>
image quality is still bad :-(
Does anybody know a workaround for this problem? My images are 48x48 png and i used them for large image icons
Thanks
Michael
Have you checked out the following page from Microsoft with specific recommendations on image size / DPI?
http://msdn.microsoft.com/en-us/library/windows/desktop/dd316921(v=vs.85).aspx
The reason for you poor quality, is because of incorrect image size. Small icons must be 16 x 16. And large icons must 32 x 32. If the image size is of another size it will be stretched. And the stretching will result in a reduced image quality.
I had the same problem and I created my own usercontrol to solve this.
This is how I did it:
<ribbon:Ribbon>
<ribbon:RibbonTab Header="File">
<ribbon:RibbonGroup Header="File">
<views:ImageButton Command="{Binding LoadCommand}" Caption="Open" SourceImage="/Images/save.png"/>
</ribbon:RibbonGroup>
</ribbon:RibbonTab>
</ribbon:Ribbon>
The image button usercontrol ImageButton.xaml
<UserControl Name="control"
<Button Command="{Binding Command, ElementName=control}" Style="{x:Null}">
<StackPanel>
<infrastructure:AutoGreyableImage Width="32" Source="{Binding SourceImage, ElementName=control}"/>
<TextBlock Text="{Binding Caption, ElementName=control}"/>
</StackPanel>
</Button>
The image button usercontrol ImageButton.xaml.cs
public partial class ImageButton : UserControl
{
public static DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(ImageButton));
public static DependencyProperty SourceProperty = DependencyProperty.Register(
"SourceImage", typeof(string), typeof(ImageButton));
public static DependencyProperty CaptionProperty = DependencyProperty.Register(
"Caption", typeof(string), typeof(ImageButton));
public ICommand Command
{
get
{
return (ICommand)GetValue(CommandProperty);
}
set
{
SetValue(CommandProperty, value);
}
}
public string SourceImage
{
get
{
return (string)GetValue(SourceProperty);
}
set
{
SetValue(SourceProperty, value);
}
}
public string Caption
{
get
{
return (string)GetValue(CaptionProperty);
}
set
{
SetValue(CaptionProperty, value);
}
}
public ImageButton()
{
InitializeComponent();
}
}
For the AutogreyableImage, I used this post
This is a copy paste of the class
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
/// <summary>
/// Class used to have an image that is able to be gray when the control is not enabled.
/// Author: Thomas LEBRUN (http://blogs.developpeur.org/tom)
/// </summary>
public class AutoGreyableImage : Image
{
/// <summary>
/// Initializes a new instance of the <see cref="AutoGreyableImage"/> class.
/// </summary>
static AutoGreyableImage()
{
// Override the metadata of the IsEnabled property.
IsEnabledProperty.OverrideMetadata(typeof(AutoGreyableImage), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnAutoGreyScaleImageIsEnabledPropertyChanged)));
}
/// <summary>
/// Called when [auto grey scale image is enabled property changed].
/// </summary>
/// <param name="source">The source.</param>
/// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void OnAutoGreyScaleImageIsEnabledPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
{
var autoGreyScaleImg = source as AutoGreyableImage;
var isEnable = Convert.ToBoolean(args.NewValue);
if (autoGreyScaleImg != null)
{
if (!isEnable)
{
// Get the source bitmap
var bitmapImage = new BitmapImage(new Uri(autoGreyScaleImg.Source.ToString()));
// Convert it to Gray
autoGreyScaleImg.Source = new FormatConvertedBitmap(bitmapImage, PixelFormats.Gray32Float, null, 0);
// Create Opacity Mask for greyscale image as FormatConvertedBitmap does not keep transparency info
autoGreyScaleImg.OpacityMask = new ImageBrush(bitmapImage);
}
else
{
// Set the Source property to the original value.
autoGreyScaleImg.Source = ((FormatConvertedBitmap)autoGreyScaleImg.Source).Source;
// Reset the Opcity Mask
autoGreyScaleImg.OpacityMask = null;
}
}
}
}
I hope this will help you and the others coming
Related
I have a need to have a XAML object always be scaled 1:1, or atleast the imagebrush content, even though it's parent is in a viewbox and the content is compressed in the X direction.
An example: Viewbox contains Label & ImageBrush. I'd like the label text to scale, but only the ImageBrush size - when zoomed out it would only display the top corner of the content.
The viewmodel for the object does not have access to the scale factor. I've been looking for a way to remove or reset the viewbox transform, but I've been unable to find one. Is there one or will I have to propagate the current scale factor from the parent to the end viewmodel? I'd rather not mix presentation logic there unless I absolutely must.
Here's the current XAML I've got so far:
<ImageBrush ImageSource="{Binding Paint, Mode=OneWay, Converter={StaticResource BitmapToImageSourceConverter}}">
<ImageBrush.RelativeTransform>
<MatrixTransform>
<MatrixTransform.Matrix>
<MultiBinding Converter="{StaticResource TimelineMatrixConverter}">
<Binding />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type MatrixTransform}}" Path="Matrix" />
</MultiBinding>
</MatrixTransform.Matrix>
</MatrixTransform>
</ImageBrush.RelativeTransform>
</ImageBrush>
Here is a basic exemple of what you can do. First extend the Viewbox class:
public class ViewboxEx : Viewbox
{
private FrameworkElement _child;
#region InvertScaleH
/// <summary>
/// InvertScaleH Read-Only Dependency Property
/// </summary>
private static readonly DependencyPropertyKey InvertScaleHPropertyKey
= DependencyProperty.RegisterReadOnly("InvertScaleH", typeof(double), typeof(ViewboxEx),
new FrameworkPropertyMetadata((double)1));
public static readonly DependencyProperty InvertScaleHProperty
= InvertScaleHPropertyKey.DependencyProperty;
/// <summary>
/// Gets the InvertScaleH property. This dependency property
/// indicates invert scale factor to compensate for horizontal scale fo the Viewbox.
/// </summary>
public double InvertScaleH
{
get { return (double)GetValue(InvertScaleHProperty); }
}
/// <summary>
/// Provides a secure method for setting the InvertScaleH property.
/// This dependency property indicates invert scale factor to compensate for horizontal scale fo the Viewbox.
/// </summary>
/// <param name="value">The new value for the property.</param>
protected void SetInvertScaleH(double value)
{
SetValue(InvertScaleHPropertyKey, value);
}
#endregion
#region InvertScaleV
/// <summary>
/// InvertScaleV Read-Only Dependency Property
/// </summary>
private static readonly DependencyPropertyKey InvertScaleVPropertyKey
= DependencyProperty.RegisterReadOnly("InvertScaleV", typeof(double), typeof(ViewboxEx),
new FrameworkPropertyMetadata((double)1));
public static readonly DependencyProperty InvertScaleVProperty
= InvertScaleVPropertyKey.DependencyProperty;
/// <summary>
/// Gets the InvertScaleV property. This dependency property
/// indicates invert scale factor to compensate for vertical scale fo the Viewbox.
/// </summary>
public double InvertScaleV
{
get { return (double)GetValue(InvertScaleVProperty); }
}
/// <summary>
/// Provides a secure method for setting the InvertScaleV property.
/// This dependency property indicates invert scale factor to compensate for vertical scale fo the Viewbox.
/// </summary>
/// <param name="value">The new value for the property.</param>
protected void SetInvertScaleV(double value)
{
SetValue(InvertScaleVPropertyKey, value);
}
#endregion
public ViewboxEx()
{
Loaded += OnLoaded;
SizeChanged += (_,__) => UpdateScale();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
UpdateChild();
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
UpdateChild();
}
private void UpdateChild()
{
if (_child != null)
{
_child.SizeChanged -= OnChild_SizeChanged;
}
_child = Child as FrameworkElement;
if (_child != null)
{
_child.SizeChanged += OnChild_SizeChanged;
}
UpdateScale();
}
private void OnChild_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateScale();
}
private void UpdateScale()
{
if (_child == null)
return;
SetInvertScaleH(_child.ActualWidth / ActualWidth);
SetInvertScaleV(_child.ActualHeight / ActualHeight);
}
}
Basically what it does is to listen for the changes of the size of the Viewbox and its content and then calculate the invert scale factor. Now to use this scale factor:
<Window x:Class="YourNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YourNamespace"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:ViewboxEx>
<Grid Width="100" Height="100">
<Ellipse Fill="Green"/>
<TextBlock Text="123" Margin="10,10,0,0">
<TextBlock.RenderTransform>
<ScaleTransform
ScaleX="{Binding InvertScaleH, RelativeSource={RelativeSource AncestorType={x:Type local:ViewboxEx}}}"
ScaleY="{Binding InvertScaleV, RelativeSource={RelativeSource AncestorType={x:Type local:ViewboxEx}}}"/>
</TextBlock.RenderTransform>
</TextBlock>
</Grid>
</local:ViewboxEx>
</Grid>
</Window>
In this exemple the Ellipse follows the Viewbox size change as usual but the text keeps its size.
This is a basic implementation and may not work in all cases.
Very high up the visual tree in WPF's Expander control is a border element (see screenshot). By default this has a CornerRadius of 3. Is it possible to modify this value?
I'll leave marking as answer for now but I managed to implement the solution as follows:
Using stylesnooper I obtained the style / control template used for the 'standard' Expander control.
Then after discovering it didn't quite behave as expected, figured out that the line <ToggleButton IsChecked="False" ... is wrong and should actually be <ToggleButton IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"...
Everything then worked as expected.
I made a behavior which modifies the first found border in the ControlTemplate. You can easily extend the behavior with new properties where u want to modify
/// <summary>
/// modifies the first found <see cref="Border"/> in the <see cref="ControlTemplate"/> of the attached <see cref="Control"/>
/// </summary>
public class ModifyBorderBehavior : Behavior<Control>
{
// ##############################################################################################################################
// Properties
// ##############################################################################################################################
#region Properties
/// <summary>
/// The new corner radius
/// </summary>
public CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
/// <summary>
/// The <see cref="CornerRadius"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(ModifyBorderBehavior));
#endregion
// ##############################################################################################################################
// Constructor
// ##############################################################################################################################
#region Constructor
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += _OnLoaded;
}
private void _OnLoaded(object sender, RoutedEventArgs e)
{
//var children = VisualTree.GetVisualChildCollection<Border>(sender);
if (sender is Control control)
{
Border border = VisualTree.GetVisualChild<Border>(control);
if(ReadLocalValue(CornerRadiusProperty) != DependencyProperty.UnsetValue)
border.CornerRadius = CornerRadius;
}
}
#endregion
}
<Expander>
<i:Interaction.Behaviors>
<zls:ModifyBorderBehavior CornerRadius="0"/>
</i:Interaction.Behaviors>
</Expander>
I am using a stack panel for displaying a slideshow. The stack panel contains two images.
Each image is bound to the View Model containing among other properties the path to the image on the disk. In the general case there are more than 2 images in the list.
There is an endless while loop used for changing the current and next pointer to the image and in this way after the slide transition is finished the the current image becomes previous and the next becomes current.
The slide transition is made by changing the scroll viewer from 0 to 100% of scrollable extended area.
This works and even very fast. It is the easiest way to implement a slide transition for an endless list of images using MVVM pattern.
My problem is that from time to time the slide transition slips and doesn't finishes at exact 100% visually but the storyboard animation completed event is triggered.
In the ViewModel I am moving the Current and Next poitners and the image blinks. Some flickering is visible from time to time.
I need to find a way on how to temporarily disable the ScrollViewer to display any visual change when I am repositioning the scoller from 100% to 0%.
I need to invalidate the WPF ScrollViewer only temporarily until I change the scroll position from 100% to 0% but this must not be visible on screen.
I mean the values must be changed but the screen must be frozen.
Is it possible somehow ?
XAML
<UserControl.Resources>
<Storyboard x:Key="sbVerticalSlideEasingTransition"
AutoReverse="True" RepeatBehavior="Forever">
<Storyboard x:Key="sbVerticalSlideTransition"
FillBehavior="HoldEnd">
<!--SCROLL TO THE MULTIPLIER OF THE HEIGHT (BASICALLY AS THE PERCENTAGE)-->
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="Mediator"
Storyboard.TargetProperty="ScrollableHeightMultiplier">
<LinearDoubleKeyFrame KeyTime="00:0:0" Value="0" />
<LinearDoubleKeyFrame KeyTime="00:0:5" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<Grid x:Name="gdContainer" Background="Black">
<ScrollViewer x:Name="Scroller" Visibility="{Binding Path=IsSlider, Mode=TwoWay, NotifyOnSourceUpdated=True, ElementName=slidePlayer,
Converter='{StaticResource BoolToVisibility}'}"
VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Disabled">
<StackPanel x:Name="ScrollPanel">
<ctrl:MediaElementEx x:Name="slide1" Height="{Binding Path=ActualHeight, ElementName=gdContainer}" Width="{Binding Path=ActualWidth, ElementName=gdContainer}" />
<ctrl:MediaElementEx x:Name="slide2" Height="{Binding Path=ActualHeight, ElementName=gdContainer}" Width="{Binding Path=ActualWidth, ElementName=gdContainer}" />
</StackPanel>
</ScrollViewer>
<!-- Mediator that forwards the property changes -->
<cmn:ScrollViewerOffsetMediator x:Name="Mediator" ScrollViewer="{Binding ElementName=Scroller}"/>
</Grid>
ScrollViewer Mediator
This is used to bind the ScrollOffset of the scrollviewer that is not bindable directly
/// <summary>
/// Mediator that forwards Offset property changes on to a ScrollViewer
/// instance to enable the animation of Horizontal/VerticalOffset.
/// </summary>
public class ScrollViewerOffsetMediator : FrameworkElement
{
/// <summary>
/// ScrollViewer instance to forward Offset changes on to.
/// </summary>
public ScrollViewer ScrollViewer
{
get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
set { SetValue(ScrollViewerProperty, value); }
}
public static readonly DependencyProperty ScrollViewerProperty =
DependencyProperty.Register(
"ScrollViewer",
typeof(ScrollViewer),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(OnScrollViewerChanged));
private static void OnScrollViewerChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
var scrollViewer = (ScrollViewer)(e.NewValue);
if (null != scrollViewer)
{
scrollViewer.ScrollToVerticalOffset(mediator.VerticalOffset);
}
}
/// <summary>
/// VerticalOffset property to forward to the ScrollViewer.
/// </summary>
public double VerticalOffset
{
get { return (double)GetValue(VerticalOffsetProperty); }
set { SetValue(VerticalOffsetProperty, value); }
}
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.Register(
"VerticalOffset",
typeof(double),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(OnVerticalOffsetChanged));
public static void OnVerticalOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
if (null != mediator.ScrollViewer)
{
mediator.ScrollViewer.ScrollToVerticalOffset((double)(e.NewValue));
}
}
/// <summary>
/// HorizontalOffset property to forward to the ScrollViewer.
/// </summary>
public double HorizontalOffset
{
get { return (double)GetValue(HorizontalOffsetProperty); }
set { SetValue(VerticalOffsetProperty, value); }
}
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.Register(
"HorizontalOffset",
typeof(double),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(OnHorizontalOffsetChanged));
public static void OnHorizontalOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
if (null != mediator.ScrollViewer)
{
mediator.ScrollViewer.ScrollToHorizontalOffset((double)(e.NewValue));
}
}
/// <summary>
/// Multiplier for ScrollableHeight property to forward to the ScrollViewer.
/// </summary>
/// <remarks>
/// 0.0 means "scrolled to top"; 1.0 means "scrolled to bottom".
/// </remarks>
public double ScrollableHeightMultiplier
{
get { return (double)GetValue(ScrollableHeightMultiplierProperty); }
set { SetValue(ScrollableHeightMultiplierProperty, value); }
}
public static readonly DependencyProperty ScrollableHeightMultiplierProperty =
DependencyProperty.Register(
"ScrollableHeightMultiplier",
typeof(double),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(OnScrollableHeightMultiplierChanged));
public static void OnScrollableHeightMultiplierChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
var scrollViewer = mediator.ScrollViewer;
if (null != scrollViewer)
{
scrollViewer.ScrollToVerticalOffset((double)(e.NewValue) * scrollViewer.ScrollableHeight);
if (((double)e.NewValue == 0) && ((double)e.OldValue == 1))
{
Debug.WriteLine("[1] {0:HH:mm:ss.fff} ScrollableHeightMultiplier 1 => 0 ", DateTime.Now);
}
}
}
/// <summary>
/// Multiplier for ScrollableWidth property to forward to the ScrollViewer.
/// </summary>
/// <remarks>
/// 0.0 means "scrolled to top"; 1.0 means "scrolled to bottom".
/// </remarks>
public double ScrollableWidthMultiplier
{
get { return (double)GetValue(ScrollableWidthMultiplierProperty); }
set { SetValue(ScrollableWidthMultiplierProperty, value); }
}
public static readonly DependencyProperty ScrollableWidthMultiplierProperty =
DependencyProperty.Register(
"ScrollableWidthMultiplier",
typeof(double),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(OnScrollableWidthMultiplierChanged));
public static void OnScrollableWidthMultiplierChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
var scrollViewer = mediator.ScrollViewer;
if (null != scrollViewer)
{
scrollViewer.ScrollToHorizontalOffset((double)(e.NewValue) * scrollViewer.ScrollableWidth);
}
}
}
Here is my issue, I created a UserControl as follows:
XAML:
<UserControl x:Class="ProcessVisualizationBar.UserControl1"
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" xmlns:lb="clr-namespace:ProcessVisualizationBar"
Name="ProcessVisualizationBar">
<Border BorderBrush="Silver" BorderThickness="1,1,1,1" Margin="0,5,5,5" CornerRadius="5" Padding="2">
<ListBox Name="ProcessVisualizationRibbon" Grid.Column="1" Height="40" ItemsSource="{Binding ElementName=ProcessVisualizationBar, Path=ItemsSource}"/>
</Border>
</UserControl>
Code Behind(C#):
using System.Windows;
using System.Windows.Controls;
namespace ProcessVisualizationBar
{
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(System.Collections.IEnumerable), typeof(UserControl));
public System.Collections.IEnumerable ItemsSource
{
get { return ProcessVisualizationRibbon.ItemsSource; }
set { ProcessVisualizationRibbon.ItemsSource = value; }
}
public UserControl1()
{
InitializeComponent();
}
}
}
I build my Usercontrol and add the .dll to the reference of another project. I add the reference at the top of my XAML as such:
xmlns:uc="clr-namespace:ProcessVisualizationBar;assembly=ProcessVisualizationBar"
Then I go to use the control.
<uc:UserControl1 Grid.Row="2" x:Name="ProcessVisualizationContent" />
It finds the control okay, but when I try and find the ItemsSource Property I added to it, I'm not finding it. I'm not sure what I missed, and I'm not sure what debug tools are really available to figure this out.
Anyone have some experience with this that can share their wisdom?
What is the actual data being passed? That is what you should be creating and not a pass through situation which you are attempting.
Create a dependency property targetting the actual data to be passed with a property changed handler. On the change event, then call internal code to bind it to the ProcessVisualazation ItemsSource. That way you can debug when the data comes through by placing a breakpoint in the event.
Here is an example where the consumer will see StringData in the Xaml and needs to pas a list of strings into the custom control:
#region public List<string> StringData
/// <summary>
/// This data is to be bound to the ribbon control
/// </summary>
public List<string> StringData
{
get { return GetValue( StringDataProperty ) as List<string>; }
set { SetValue( StringDataProperty, value ); }
}
/// <summary>
/// Identifies the StringData dependency property.
/// </summary>
public static readonly System.Windows.DependencyProperty StringDataProperty =
System.Windows.DependencyProperty.Register(
"StringData",
typeof( List<string> ),
typeof( UserControl ),
new System.Windows.PropertyMetadata( null, OnStringDataPropertyChanged ) );
/// <summary>
/// StringDataProperty property changed handler.
/// </summary>
/// <param name="d">DASTreeBinder that changed its StringData.</param>
/// <param name="e">Event arguments.</param>
private static void OnStringDataPropertyChanged( System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e )
{
UserControl source = d as UserControl;
List<string> value = e.NewValue as List<string>;
BindDataToRibbon( value );
}
#endregion public List<string> StringData
Now just create a BindDataToRibbon method which will do the dirty work. Note that I use Jeff Wilcox's Silverlight dependency snippets in Visual Studio to generate the above dependency. I have used it for WPF and Silverlight projects.
I have a WPF application where I need to do something like that :
<ItemsControl x:Name="lstProducts">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ProductName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I set the ItemsSource in code like this : lstProducts.ItemsSource = MyEFContext.Products;
Up to now everything is working fine. Now I want to use my own UserControl to display a product instead of the TextBlock like that.
<ItemsControl x:Name="lstProducts">
<ItemsControl.ItemTemplate>
<DataTemplate>
<my:ProductItemCtl ProductName="{Binding ProductName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In my userControl, I created a DependencyProperty like that (see below) where I set the ProductName in the OnProductNameChanged callback.
The TextBlock in my usercontrol is not updated when binding the ItemsControl, and the callback is not launched.
#region ProductName
/// <summary>
/// ProductName Dependency Property
/// </summary>
public static readonly DependencyProperty ProductNameProperty =
DependencyProperty.Register("ProductName", typeof(String), typeof(ProductItemCtl),
new FrameworkPropertyMetadata("",
new PropertyChangedCallback(OnProductNameChanged)));
/// <summary>
/// Gets or sets the ProductName property. This dependency property
/// indicates ....
/// </summary>
public String ProductName
{
get { return (String)GetValue(ProductNameProperty); }
set { SetValue(ProductNameProperty, value); }
}
/// <summary>
/// Handles changes to the ProductName property.
/// </summary>
private static void OnProductNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ProductItemCtl target = (ProductItemCtl)d;
String oldProductName = (String)e.OldValue;
String newProductName = target.ProductName;
target.OnProductNameChanged(oldProductName, newProductName);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes to the ProductName property.
/// </summary>
protected virtual void OnProductNameChanged(String oldProductName, String newProductName)
{
// Set Product Name in the display Here!
this.txtProductName.Text = newProductName;
}
#endregion
Not an answer, but i can't write this as comment. What happens if you simplify your code like this:
public static readonly DependencyProperty ProductNameProperty = DependencyProperty.Register(
"ProductName", typeof(string), typeof(ProductItemCtl),
new FrameworkPropertyMetadata(string.Empty,
(o, e) => ((ProductItemCtl)o).txtProductName.Text = (string)e.NewValue));
Arrrghhh got it... I had this.ProductName = ""; in the constructor of my UserControl
I wasted so much time on this... And sorry for wasting all of yours.
Since I found out by trying your code Clemens I will mark your answer as "Answered".
thanks everyone.