CompositeTransform is only used for silverlight?. Is there anyway we can use that in WPF or any equivalent replacement?
There is no CompositeTransform in WPF however there is a TransformGroup. Hence an equivalent replacement is a TransformGroup containing ScaleTransform, SkewTransform, RotateTransform and TranslateTransform in that order.
Here is a much nicer solution if you are anal about code cleanliness:
http://www.singulink.com/CodeIndex/post/getting-rid-of-ugly-transformgroup-blocks-in-wpf
Its easy on the eyes and because it just returns a TransformGroup, you can still use the Blend designer to work with animating over the transform!
<Rectangle Width="100" Height="100" Fill="LightGreen"
RenderTransform="{data:CompositeTransform ScaleX=2.5, ScaleY=1, SkewX=-60, Rotation=145}"
RenderTransformOrigin="0.5,0.5" />
Implementation:
public class CompositeTransformExtension : MarkupExtension
{
public double CenterX
{
get { return _scale.CenterX; }
set
{
_scale.CenterX = value;
_skew.CenterX = value;
_rotate.CenterX = value;
}
}
public double CenterY
{
get { return _scale.CenterY; }
set
{
_scale.CenterY = value;
_skew.CenterY = value;
_rotate.CenterY = value;
}
}
public double ScaleX
{
get { return _scale.ScaleX; }
set { _scale.ScaleX = value; }
}
public double ScaleY
{
get { return _scale.ScaleY; }
set { _scale.ScaleY = value; }
}
public double SkewX
{
get { return _skew.AngleX; }
set { _skew.AngleX = value; }
}
public double SkewY
{
get { return _skew.AngleY; }
set { _skew.AngleY = value; }
}
public double Rotation
{
get { return _rotate.Angle; }
set { _rotate.Angle = value; }
}
public double TranslateX
{
get { return _translate.X; }
set { _translate.X = value; }
}
public double TranslateY
{
get { return _translate.Y; }
set { _translate.Y = value; }
}
private ScaleTransform _scale = new ScaleTransform();
private SkewTransform _skew = new SkewTransform();
private RotateTransform _rotate = new RotateTransform();
private TranslateTransform _translate = new TranslateTransform();
public CompositeTransformExtension()
{
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var group = new TransformGroup();
group.Children.Add(_scale);
group.Children.Add(_skew);
group.Children.Add(_rotate);
group.Children.Add(_translate);
return group;
}
}
Related
I'm currently working on a kind of VirtualizedWrapPanel to use as the ItemsPanel in a ListView.
After following this guy's instructions, and borrowing heavily from this guy's implementation found on codeproject but I don't have the reputation to post the link so sorry..., I have something that is nicely shaping up to be exactly what I need.
The item size is fixed so the scrolling is pixel based. the orientation is always horizontal.
the ListView :
<ListView Name="lv"
ItemsSource="{Binding CV}"
IsSynchronizedWithCurrentItem="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<i:Interaction.Behaviors>
<local:ScrollToSelectionListViewBehavior/> <!-- Behavior calling ScrollIntoView whenever the selection changes -->
<local:ListViewSelectedItemsBehavior SelectedItems="{Binding SelectedItems}"/> <!-- Behavior exposing the attached ListView's SelectedItems array -->
</i:Interaction.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Height="100" Width="100" Orientation="Vertical">
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Width="90" Height="90">
<TextBlock Text ="{Binding ItemText}" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<local:VirtualizingWrapPanel ItemHeight="100" ItemWidth="110" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
the local:VirtualizingWrapPanel :
public class VirtualizingWrapPanel : VirtualizingPanel, IScrollInfo
{
private ScrollViewer _owner;
private const bool _canHScroll = false;
private bool _canVScroll = false;
private Size _extent = new Size(0, 0);
private Size _viewport = new Size(0, 0);
private Point _offset;
UIElementCollection _children;
ItemsControl _itemsControl;
IItemContainerGenerator _generator;
Dictionary<UIElement, Rect> _realizedChildLayout = new Dictionary<UIElement, Rect>();
#region Properties
private Size ChildSlotSize
{
get { return new Size(ItemWidth, ItemHeight); }
}
#endregion
#region Dependency Properties
[TypeConverter(typeof(LengthConverter))]
public double ItemHeight
{
get { return (double)base.GetValue(ItemHeightProperty); }
set { base.SetValue(ItemHeightProperty, value); }
}
[TypeConverter(typeof(LengthConverter))]
public double ItemWidth
{
get { return (double)base.GetValue(ItemWidthProperty); }
set { base.SetValue(ItemWidthProperty, value); }
}
public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register("ItemHeight", typeof(double), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(double.PositiveInfinity));
public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register("ItemWidth", typeof(double), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(double.PositiveInfinity));
private int LineCapacity
{ get { return Math.Max((_viewport.Width != 0) ? (int)(_viewport.Width / ItemWidth) : 0, 1); } }
private int LinesCount
{ get { return (ItemsCount > 0) ? ItemsCount / LineCapacity : 0 ; } }
private int ItemsCount
{ get { return _itemsControl.Items.Count; } }
public int FirstVisibleLine
{ get { return (int)(_offset.Y / ItemHeight); } }
public int FirstVisibleItemVPos
{ get { return (int)((FirstVisibleLine * ItemHeight) - _offset.Y); } }
public int FirstVisibleIndex
{ get { return (FirstVisibleLine * LineCapacity); } }
#endregion
#region VirtualizingPanel overrides
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
_itemsControl = ItemsControl.GetItemsOwner(this);
_children = InternalChildren;
_generator = ItemContainerGenerator;
this.SizeChanged += new SizeChangedEventHandler(this.Resizing);
}
protected override Size MeasureOverride(Size availableSize)
{
if (_itemsControl == null || _itemsControl.Items.Count == 0)
return availableSize;
if (availableSize != _viewport)
{
_viewport = availableSize;
if (_owner != null)
_owner.InvalidateScrollInfo();
}
Size childSize = new Size(ItemWidth, ItemHeight);
Size extent = new Size(availableSize.Width, LinesCount * ItemHeight);
if (extent != _extent)
{
_extent = extent;
if (_owner != null)
_owner.InvalidateScrollInfo();
}
foreach (UIElement child in this.InternalChildren)
{
child.Measure(childSize);
}
_realizedChildLayout.Clear();
Size realizedFrameSize = availableSize;
int firstVisibleIndex = FirstVisibleIndex;
GeneratorPosition startPos = _generator.GeneratorPositionFromIndex(firstVisibleIndex);
int childIndex = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1;
int current = firstVisibleIndex;
using (_generator.StartAt(startPos, GeneratorDirection.Forward, true))
{
bool stop = false;
double currentX = 0;
double currentY = FirstVisibleItemVPos;
while (current < ItemsCount)
{
bool newlyRealized;
// Get or create the child
UIElement child = _generator.GenerateNext(out newlyRealized) as UIElement;
if (newlyRealized)
{
// Figure out if we need to insert the child at the end or somewhere in the middle
if (childIndex >= _children.Count)
{
base.AddInternalChild(child);
}
else
{
base.InsertInternalChild(childIndex, child);
}
_generator.PrepareItemContainer(child);
child.Measure(ChildSlotSize);
}
else
{
// The child has already been created, let's be sure it's in the right spot
Debug.Assert(child == _children[childIndex], "Wrong child was generated");
}
childSize = child.DesiredSize;
Rect childRect = new Rect(new Point(currentX, currentY), childSize);
if (childRect.Right > realizedFrameSize.Width) //wrap to a new line
{
currentY = currentY + ItemHeight;
currentX = 0;
childRect.X = currentX;
childRect.Y = currentY;
}
if (currentY > realizedFrameSize.Height)
stop = true;
currentX = childRect.Right;
_realizedChildLayout.Add(child, childRect);
if (stop)
break;
current++;
childIndex++;
}
}
CleanUpItems(firstVisibleIndex, current - 1);
return availableSize;
}
public void CleanUpItems(int minDesiredGenerated, int maxDesiredGenerated)
{
for (int i = _children.Count - 1; i >= 0; i--)
{
GeneratorPosition childGeneratorPos = new GeneratorPosition(i, 0);
int itemIndex = _generator.IndexFromGeneratorPosition(childGeneratorPos);
if (itemIndex < minDesiredGenerated || itemIndex > maxDesiredGenerated)
{
//var c = _children[i] as ListViewItem;
//if(c!= null && c.IsSelected)
//{
//}
_generator.Remove(childGeneratorPos, 1);
RemoveInternalChildRange(i, 1);
}
}
}
protected override Size ArrangeOverride(Size finalSize)
{
if (finalSize != _viewport)
{
_viewport = finalSize;
if (_owner != null)
_owner.InvalidateScrollInfo();
}
Size childSize = new Size(ItemWidth, ItemHeight);
Size extent = new Size(finalSize.Width, LinesCount * ItemHeight);
if (extent != _extent)
{
_extent = extent;
if (_owner != null)
_owner.InvalidateScrollInfo();
}
if (_children != null)
{
foreach (UIElement child in _children)
{
var layoutInfo = _realizedChildLayout[child];
child.Arrange(layoutInfo);
}
}
return finalSize;
}
protected override void BringIndexIntoView(int index)
{
SetVerticalOffset((index / LineCapacity) * ItemHeight);
}
protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args)
{
base.OnItemsChanged(sender, args);
_offset.X = 0;
_offset.Y = 0;
switch (args.Action)
{
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Replace:
RemoveInternalChildRange(args.Position.Index, args.ItemUICount);
break;
case NotifyCollectionChangedAction.Move:
RemoveInternalChildRange(args.OldPosition.Index, args.ItemUICount);
break;
}
}
#endregion
#region EventHandlers
public void Resizing(object sender, EventArgs e)
{
var args = e as SizeChangedEventArgs;
if(args.WidthChanged)
{
int lineCapacity = LineCapacity;
int previousLineCapacity = (int)(args.PreviousSize.Width / ItemWidth);
if (previousLineCapacity != lineCapacity)
{
int previousFirstItem = ((int)(_offset.Y / ItemHeight) <= 0) ? 0 : ((int)(_offset.Y / ItemHeight) * previousLineCapacity);
BringIndexIntoView(previousFirstItem);
}
}
if (_viewport.Width != 0)
{
MeasureOverride(_viewport);
}
}
#endregion
#region IScrollInfo Implementation
public ScrollViewer ScrollOwner
{
get { return _owner; }
set { _owner = value; }
}
public bool CanHorizontallyScroll
{
get { return false; }
set { if (value == true) throw (new ArgumentException("VirtualizingWrapPanel does not support Horizontal scrolling")); }
}
public bool CanVerticallyScroll
{
get { return _canVScroll; }
set { _canVScroll = value; }
}
public double ExtentHeight
{
get { return _extent.Height;}
}
public double ExtentWidth
{
get { return _extent.Width; }
}
public double HorizontalOffset
{
get { return _offset.X; }
}
public double VerticalOffset
{
get { return _offset.Y; }
}
public double ViewportHeight
{
get { return _viewport.Height; }
}
public double ViewportWidth
{
get { return _viewport.Width; }
}
public Rect MakeVisible(Visual visual, Rect rectangle)
{
var gen = (ItemContainerGenerator)_generator.GetItemContainerGeneratorForPanel(this);
var element = (UIElement)visual;
int itemIndex = gen.IndexFromContainer(element);
while (itemIndex == -1)
{
element = (UIElement)VisualTreeHelper.GetParent(element);
itemIndex = gen.IndexFromContainer(element);
}
Rect elementRect = _realizedChildLayout[element];
if (elementRect.Bottom > ViewportHeight)
{
double translation = elementRect.Bottom - ViewportHeight;
_offset.Y += translation;
}
else if (elementRect.Top < 0)
{
double translation = elementRect.Top;
_offset.Y += translation;
}
InvalidateMeasure();
return elementRect;
}
public void LineDown()
{
SetVerticalOffset(VerticalOffset + 50);
}
public void LineUp()
{
SetVerticalOffset(VerticalOffset - 50);
}
public void MouseWheelDown()
{
SetVerticalOffset(VerticalOffset + 50);
}
public void MouseWheelUp()
{
SetVerticalOffset(VerticalOffset - 50);
}
public void PageDown()
{
int fullyVisibleLines = (int)(_viewport.Height / ItemHeight);
SetVerticalOffset(VerticalOffset + (fullyVisibleLines * ItemHeight));
}
public void PageUp()
{
int fullyVisibleLines = (int)(_viewport.Height / ItemHeight);
SetVerticalOffset(VerticalOffset - (fullyVisibleLines * ItemHeight));
}
public void SetVerticalOffset(double offset)
{
if (offset < 0 || _viewport.Height >= _extent.Height)
{
offset = 0;
}
else
{
if (offset + _viewport.Height >= _extent.Height)
{
offset = _extent.Height - _viewport.Height;
}
}
_offset.Y = offset;
if (_owner != null)
_owner.InvalidateScrollInfo();
InvalidateMeasure();
}
public void LineLeft() { throw new NotImplementedException(); }
public void LineRight() { throw new NotImplementedException(); }
public void MouseWheelLeft() { throw new NotImplementedException(); }
public void MouseWheelRight() { throw new NotImplementedException(); }
public void PageLeft() { throw new NotImplementedException(); }
public void PageRight() { throw new NotImplementedException(); }
public void SetHorizontalOffset(double offset) { throw new NotImplementedException(); }
#endregion
#region methods
#endregion
}
Now my problem is : An Item Selection should always Deselect the previously selected item, when using a normal WrapPanel, the previously selected ListViewItem's IsSelected property is always set to false before the new selected ListViewItem's IsSelected is set to true.
This deselection does not happen with my VirtualizingPanel when the previously selected item is no longer realized (when it is not visible in the viewport), so I end up with two or more selected items at once and the panel's behavior becomes really weird. Sometimes it even settles into an infinite loop, the two selected items yanking visibility from each other in a never ending battle.
I searched a bit for a solution to this problem but I don't really know where to start or what to search for.
Here is a test project if you want to experiment with it.
Thanks
I found a way by preventing the virtualization of selected items. Now the control behaves correctly.
public void CleanUpItems(int minDesiredGenerated, int maxDesiredGenerated)
{
for (int i = _children.Count - 1; i >= 0; i--)
{
GeneratorPosition childGeneratorPos = new GeneratorPosition(i, 0);
int itemIndex = _generator.IndexFromGeneratorPosition(childGeneratorPos);
if (itemIndex < minDesiredGenerated || itemIndex > maxDesiredGenerated)
{
//I don't much like this cast
// how do I access the IsSelectedProperty
// of a UIElement of any type ( ListBoxItem, TreeViewItem...)?
var c = _children[i] as ListViewItem;
c.IsEnabled = false;
if (c != null && c.IsSelected)
{
var layoutInfo = new Rect(0, 0, 0, 0);
c.Arrange(layoutInfo);
}
else
{
_generator.Remove(childGeneratorPos, 1);
RemoveInternalChildRange(i, 1);
}
}
}
}
In ArrangeOverride :
if (_children != null)
{
foreach (UIElement child in _children)
{
if (child.IsEnabled)
{
var layoutInfo = _realizedChildLayout[child];
child.Arrange(layoutInfo);
}
}
}
In MeasureOverride:
while (current < ItemsCount)
{
bool newlyRealized;
// Get or create the child
UIElement child = _generator.GenerateNext(out newlyRealized) as UIElement;
child.IsEnabled = true;
I'm still curious to know if there's a better way. In the meantime this will do.
btw I'll update the test project with this fix in case anyone wants a virtualizing wrappanel.
I want to dynamically load and store the Datagrid column widths from my ini file. Write to my inifile for every resize of the column width. Which event can i ues for this. Could any body give any Suggetions or sample code for this.
i use apllicationsettings within a behavior for such things and save the information on application exit.
usage
<DataGrid>
<i:Interaction.Behaviors>
<local:DataGridBehavior GridSettings="{Binding Source={x:Static local:MySettings.Instance},Mode=OneWay}" />
</i:Interaction.Behaviors>
</DataGrid>
settings
[SettingsManageabilityAttribute(SettingsManageability.Roaming)]
public sealed class MySettings: ApplicationSettingsBase, IGridSettings
{
private static readonly Lazy<MySettings> LazyInstance = new Lazy<MySettings>(() => new KadiaSettings());
public static MySettingsInstance { get { return LazyInstance.Value; } }
[UserScopedSettingAttribute()]
[DefaultSettingValue("")]
[SettingsSerializeAs(SettingsSerializeAs.Xml)]
public SerializableDictionary<string, int> GridDisplayIndexList
{
get { return (SerializableDictionary<string, int>)this["GridDisplayIndexList"]; }
set { this["GridDisplayIndexList"] = value; }
}
[UserScopedSettingAttribute()]
[DefaultSettingValue("")]
[SettingsSerializeAs(SettingsSerializeAs.Xml)]
public SerializableDictionary<string, Visibility> GridColumnVisibilityList
{
get { return (SerializableDictionary<string, Visibility>)this["GridColumnVisibilityList"]; }
set { this["GridColumnVisibilityList"] = value; }
}
[UserScopedSettingAttribute()]
[DefaultSettingValue("")]
[SettingsSerializeAs(SettingsSerializeAs.Xml)]
public SerializableDictionary<string, double> GridColumnWidthList
{
get { return (SerializableDictionary<string, double>)this["GridColumnWidthList"]; }
set { this["GridColumnWidthList"] = value; }
}
private MySettings()
{
Application.Current.Exit += OnExit;
}
private void OnExit(object sender, ExitEventArgs e)
{
this.Save();
}
}
public interface IGridSettings: INotifyPropertyChanged
{
SerializableDictionary<string, int> GridDisplayIndexList { get; }
SerializableDictionary<string, Visibility> GridColumnVisibilityList { get; }
SerializableDictionary<string, double> GridColumnWidthList { get; }
}
[XmlRoot("Dictionary")]
public class SerializableDictionary<TKey, TValue>: Dictionary<TKey, TValue>, IXmlSerializable
{
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
var keySerializer = new XmlSerializer(typeof(TKey));
var valueSerializer = new XmlSerializer(typeof(TValue));
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
reader.ReadStartElement("item");
reader.ReadStartElement("key");
var key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
var value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
this.Add(key, value);
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
var keySerializer = new XmlSerializer(typeof(TKey));
var valueSerializer = new XmlSerializer(typeof(TValue));
foreach (TKey key in this.Keys)
{
writer.WriteStartElement("item");
writer.WriteStartElement("key");
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
TValue value = this[key];
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
#endregion
}
behavior
public class DataGridBehavior : Behavior<DataGrid>
{
public static readonly DependencyProperty GridSettingsProperty =
DependencyProperty.Register("GridSettings", typeof(IGridSettings), typeof(DataGridBehavior), null);
public IGridSettings GridSettings
{
get { return (IGridSettings)GetValue(GridSettingsProperty); }
set { SetValue(GridSettingsProperty, value); }
}
public DataGridICollectionViewSortMerkerBehavior()
{
Application.Current.Exit += CurrentExit;
}
private void CurrentExit(object sender, ExitEventArgs e)
{
SetSettings();
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObjectLoaded;
AssociatedObject.Unloaded += AssociatedObjectUnloaded;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= AssociatedObjectLoaded;
AssociatedObject.Unloaded -= AssociatedObjectUnloaded;
}
private void AssociatedObjectUnloaded(object sender, RoutedEventArgs e)
{
SetSettings();
}
void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
{
var settings = GridSettings;
var columns = AssociatedObject.Columns.ToList();
var colCount = columns.Count;
foreach (var column in columns)
{
var key = column.Header.ToString();
if (settings.GridDisplayIndexList.ContainsKey(key))
{
//manchmal wird -1 als index abgespeichert
var index = settings.GridDisplayIndexList[key];
if(index > 0 && index < colCount)
column.DisplayIndex = index;
}
if (settings.GridColumnVisibilityList.ContainsKey(key))
{
column.Visibility = settings.GridColumnVisibilityList[key];
}
if (settings.GridColumnWidthList.ContainsKey(key))
{
column.Width = new DataGridLength(settings.GridColumnWidthList[key]);
}
}
}
private void SetSettings()
{
var settings = GridSettings;
foreach (var column in AssociatedObject.Columns)
{
var key = column.Header.ToString();
var displayindex = column.DisplayIndex;
var visibility = column.Visibility;
var width = column.ActualWidth;
if (settings.GridDisplayIndexList.ContainsKey(key))
{
settings.GridDisplayIndexList[key] = displayindex;
}
else
{
settings.GridDisplayIndexList.Add(key, displayindex);
}
if (settings.GridColumnVisibilityList.ContainsKey(key))
{
settings.GridColumnVisibilityList[key] = visibility;
}
else
{
settings.GridColumnVisibilityList.Add(key, visibility);
}
if (settings.GridColumnWidthList.ContainsKey(key))
{
settings.GridColumnWidthList[key] = width;
}
else
{
settings.GridColumnWidthList.Add(key, width);
}
}
}
}
I'm writing a user control for animation.
Its uses an internal ImageList to store the animation images and paint one after the other in a loop.
This is the whole code:
public partial class Animator : UserControl
{
public event EventHandler OnLoopElapsed = delegate { };
private ImageList imageList = new ImageList();
private Timer timer;
private bool looping = true;
private int index;
private Image newImage;
private Image oldImage;
public Animator()
{
InitializeComponent();
base.DoubleBuffered = true;
timer = new Timer();
timer.Tick += timer_Tick;
timer.Interval = 50;
}
public bool Animate
{
get { return timer.Enabled; }
set
{
index = 0;
timer.Enabled = value;
}
}
public int CurrentIndex
{
get { return index; }
set { index = value; }
}
public ImageList ImageList
{
set
{
imageList = value;
Invalidate();
index = 0;
}
get { return imageList; }
}
public bool Looping
{
get { return looping; }
set { looping = value; }
}
public int Interval
{
get { return timer.Interval; }
set { timer.Interval = value; }
}
private void timer_Tick(object sender, EventArgs e)
{
if (imageList.Images.Count == 0)
return;
Invalidate(true);
index++;
if (index >= imageList.Images.Count)
{
if (looping)
index = 0;
else
timer.Stop();
OnLoopElapsed(this, EventArgs.Empty);
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (oldImage != null)
e.Graphics.DrawImage(oldImage, ClientRectangle);
else
e.Graphics.Clear(BackColor);
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
if (imageList.Images.Count > 0)
{
newImage = imageList.Images[index];
g.DrawImage(newImage, ClientRectangle);
oldImage = newImage;
}
else
{
e.Graphics.Clear(BackColor);
}
}
}
The animation seems very nice and smooth,
but the problem is that its surrounding rectangle is painted black.
What am I missing here?
I've seen very smooth transparent animation done here in WPF,
I've placed some label behind it and they are seen thru the rotating wheel as I hoped.
But I don't know WPF well enough to build such a control in WPF.
Any idea or WPF sample code will be appreciated.
This was solved by removing this line from the constructor:
base.DoubleBuffered = true;
Now the control is fully transparent, even while changing its images.
In my canonical example, I have two tabs the first with a button and the second with a text box that is bound to a validation rule. When the button is clicked an action occurs that should cause the validation to fail and therefore the Validation.Error event to fire. However, the event will only fire when I click the second tab.
The reason this is so important to me is I have a form with complex validation that occurs across multiple tabs and I want to highlight those tabs containing errors in some - and I especially want to display the errors when the form first loads.
I've already used a technique to force the validation to fire when the form loads but I just don't know why when the forms loaded it doesn't fire when the button is clicked.
The XAML for my test case :
<Window x:Class="WpfApplication1.TabsDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Tabs" Height="300" Width="300" Validation.Error="Window_Error">
<TabControl Grid.Row="0">
<TabItem>
<TabItem.Header>First</TabItem.Header>
<StackPanel Margin="5">
<Button Click="Button_Click">Clear the second textbox</Button>
</StackPanel>
</TabItem>
<TabItem>
<TabItem.Header>MyDataItem</TabItem.Header>
<TextBox>
<TextBox.Text>
<local:ValidationBinding Path="MyDataItem" UpdateSourceTrigger="LostFocus">
<local:ValidationBinding.ValidationRules>
<local:ValidateText />
</local:ValidationBinding.ValidationRules>
</local:ValidationBinding>
</TextBox.Text>
</TextBox>
</TabItem>
</TabControl>
</Window>
My code behind :
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class TabsDemo : Window
{
public TabsDemo()
{
InitializeComponent();
DataContext = new MyViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
(DataContext as MyViewModel).MyDataItem = String.Empty;
}
private void Window_Error(object sender, ValidationErrorEventArgs e)
{
MessageBox.Show("Validation Error : " + e.Error.RuleInError);
}
}
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _myDataItem = "Default Value";
public string MyDataItem
{
get { return _myDataItem; }
set
{
if (_myDataItem != value)
{
_myDataItem = value;
NotifyPropertyChanged(new PropertyChangedEventArgs("MyDataItem"));
}
}
}
private void NotifyPropertyChanged(PropertyChangedEventArgs args)
{
if (PropertyChanged != null)
PropertyChanged(this, args);
}
}
}
And for completeness here's the validation binding markup extension :
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
namespace WpfApplication1
{
public class ValidationBinding : MarkupExtension
{
private readonly Binding _binding = new Binding();
private DependencyObject _dependencyObject;
private DependencyProperty _dependencyProperty;
public ValidationBinding()
{
_binding.ValidatesOnDataErrors = true;
_binding.ValidatesOnExceptions = true;
_binding.NotifyOnValidationError = true;
}
public ValidationBinding(string path)
{
_binding.Path = new PropertyPath(path);
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var valueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
_dependencyObject = valueTarget.TargetObject as DependencyObject;
_dependencyProperty = valueTarget.TargetProperty as DependencyProperty;
var element = _dependencyObject as FrameworkElement;
if (element != null)
{
if (element.IsLoaded)
ForceValidation();
else
element.Loaded += (sender, args) => ForceValidation();
}
else
{
ForceValidation();
}
Debug.WriteLine("MarkupExtension.ProvideValue called for element " + element.Name);
return _binding.ProvideValue(serviceProvider);
}
private void ForceValidation()
{
BindingOperations.GetBindingExpression(_dependencyObject, _dependencyProperty).UpdateSource();
}
public object FallbackValue
{
get { return _binding.FallbackValue; }
set { _binding.FallbackValue = value; }
}
public string StringFormat
{
get { return _binding.StringFormat; }
set { _binding.StringFormat = value; }
}
public object TargetNullValue
{
get { return _binding.TargetNullValue; }
set { _binding.TargetNullValue = value; }
}
public string BindingGroupName
{
get { return _binding.BindingGroupName; }
set { _binding.BindingGroupName = value; }
}
public Collection<ValidationRule> ValidationRules
{
get { return _binding.ValidationRules; }
}
public bool ValidatesOnExceptions
{
get { return _binding.ValidatesOnExceptions; }
set { _binding.ValidatesOnExceptions = value; }
}
public bool ValidatesOnDataErrors
{
get { return _binding.ValidatesOnDataErrors; }
set { _binding.ValidatesOnDataErrors = value; }
}
public PropertyPath Path
{
get { return _binding.Path; }
set { _binding.Path = value; }
}
public string XPath
{
get { return _binding.XPath; }
set { _binding.XPath = value; }
}
public BindingMode Mode
{
get { return _binding.Mode; }
set { _binding.Mode = value; }
}
public UpdateSourceTrigger UpdateSourceTrigger
{
get { return _binding.UpdateSourceTrigger; }
set { _binding.UpdateSourceTrigger = value; }
}
public bool NotifyOnSourceUpdated
{
get { return _binding.NotifyOnSourceUpdated; }
set { _binding.NotifyOnSourceUpdated = value; }
}
public bool NotifyOnTargetUpdated
{
get { return _binding.NotifyOnTargetUpdated; }
set { _binding.NotifyOnTargetUpdated = value; }
}
public bool NotifyOnValidationError
{
get { return _binding.NotifyOnValidationError; }
set { _binding.NotifyOnValidationError = value; }
}
public IValueConverter Converter
{
get { return _binding.Converter; }
set { _binding.Converter = value; }
}
public object ConverterParameter
{
get { return _binding.ConverterParameter; }
set { _binding.ConverterParameter = value; }
}
public CultureInfo ConverterCulture
{
get { return _binding.ConverterCulture; }
set { _binding.ConverterCulture = value; }
}
public object Source
{
get { return _binding.Source; }
set { _binding.Source = value; }
}
public RelativeSource RelativeSource
{
get { return _binding.RelativeSource; }
set { _binding.RelativeSource = value; }
}
public string ElementName
{
get { return _binding.ElementName; }
set { _binding.ElementName = value; }
}
public bool IsAsync
{
get { return _binding.IsAsync; }
set { _binding.IsAsync = value; }
}
public object AsyncState
{
get { return _binding.AsyncState; }
set { _binding.AsyncState = value; }
}
public bool BindsDirectlyToSource
{
get { return _binding.BindsDirectlyToSource; }
set { _binding.BindsDirectlyToSource = value; }
}
public UpdateSourceExceptionFilterCallback UpdateSourceExceptionFilter
{
get { return _binding.UpdateSourceExceptionFilter; }
set { _binding.UpdateSourceExceptionFilter = value; }
}
}
}
I've encountered a strange problem working with adorners.By using a class inheriting from Adorner,I try to draw a line in a ScrollViewer dynamically.It works well when the line is shorter than a particular length(about 600),but if I tried to scroll the ScrollViewer and draw the line longer,the problem came.The line appeared to be clipped and the rest part which is longer than 600 seemed to be invisible. I had checked the line's X1,X2,Y1,Y2,it seemed to be correct.Could you please give some advices?Thanks a lot!
Here is my Adorner Class
internal class LinkGanttTaskAdorner : Adorner
{
private readonly Line child = null;
private double x2 = 0;
private double y2 = 0;
private double x1 = 0;
private double y1 = 0;
public LinkGanttTaskAdorner(UIElement adornedElement)
: base(adornedElement)
{
var brush = new VisualBrush(adornedElement);
child = new Line
{
Stroke = Brushes.Black,
StrokeThickness = 1
};
}
protected override Size MeasureOverride(Size constraint)
{
child.Measure(constraint);
return child.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
child.Arrange(new Rect(finalSize));
return finalSize;
}
protected override Visual GetVisualChild(int index)
{
return child;
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
public double X1
{
get
{
return x1;
}
set
{
x1 = value;
child.X1 = value;
}
}
public double Y1
{
get
{
return y1;
}
set
{
y1 = value;
child.Y1 = value;
}
}
public double X2
{
get
{
return x2;
}
set
{
x2 = value;
child.X2 = value;
UpdatePosition();
}
}
public double Y2
{
get
{
return y2;
}
set
{
y2 = value;
child.Y2 = value;
UpdatePosition();
}
}
private void UpdatePosition()
{
var adornerLayer = this.Parent as AdornerLayer;
if (adornerLayer != null)
{
adornerLayer.Update(AdornedElement);
}
}
By using the MouseMove event to Draw the line
private void DragLinkStarted()
{
isMoveTask = false;
isLinkTask = true;
linkAdorner = new LinkGanttTaskAdorner(originalElement) { X1 = startMousePoint.X, Y1 = startMousePoint.Y };
var layer = AdornerLayer.GetAdornerLayer(originalElement);
layer.Add(linkAdorner);
Cursor = Cursors.Cross;
}
private void DragLinkMoved()
{
mousePoint = Mouse.GetPosition(originalElement);
linkAdorner.X2 = mousePoint.X;
linkAdorner.Y2 = mousePoint.Y;
}