Как контейнер узнает, когда ребенок вызвал InvalidateArrange?
Я пытаюсь изучить основы создания пользовательской панели в приложении WinRT XAML. Я определил вложенное свойство зависимостей, и оно работает, как и ожидалось, за исключением того, что я не могу понять, как получить обратный вызов свойства для дочернего элемента, чтобы инициировать расположение или меру контейнера.
Какой правильный способ дать ребенку понять, что его контейнер и меры должны быть вызваны снова? В моей открытой книге WPF 4 они используют FrameworkPropertyMetadataOptions.AffectsParentArrange, но в WinRT это, по-видимому, недоступно.
public class SimpleCanvas : Panel
{
#region Variables
#region Left Property
public static double GetLeft(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
object value = element.GetValue(LeftProperty);
Type valueType = value.GetType();
return Convert.ToDouble(value);
}
public static void SetLeft(UIElement element, double value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(LeftProperty, value);
}
public static readonly DependencyProperty LeftProperty =
DependencyProperty.RegisterAttached("Left", typeof(double), typeof(SimpleCanvas),
new PropertyMetadata(0, OnLeftPropertyChanged));
public static void OnLeftPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
UIElement element = (UIElement)source;
// This doesn't cause ArrangeOverride below to be called
element.InvalidateArrange();
}
#endregion
#region Top Property
public static double GetTop(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
object value = element.GetValue(TopProperty);
return (value == null) ? 0 : (double)value;
}
public static void SetTop(UIElement element, double value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(TopProperty, value);
}
public static readonly DependencyProperty TopProperty =
DependencyProperty.RegisterAttached("Top", typeof(double), typeof(SimpleCanvas),
new PropertyMetadata(0, OnTopPropertyChanged));
public static void OnTopPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
UIElement element = (UIElement)source;
// This doesn't cause ArrangeOverride below to be called
element.InvalidateArrange();
}
#endregion
#endregion
public SimpleCanvas()
{
}
#region Methods
protected override Size MeasureOverride(Size availableSize)
{
foreach (UIElement child in this.Children)
{
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
return new Size(0, 0);
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in this.Children)
{
double x = 0;
double y = 0;
double left = GetLeft(child);
double top = GetTop(child);
if (!double.IsNaN(left))
{
x = left;
}
if (!double.IsNaN(top))
{
y = top;
}
child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
}
return finalSize;
}
#endregion
}
3 ответа
Я опоздал на вечеринку, но я пошел в том же направлении и столкнулся с той же проблемой. Вот мое решение.
При обратном вызове вы вызываете InvalidateArrange для дочернего элемента, к которому вы прикрепили свойство:
public static void OnTopPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
UIElement element = (UIElement)source;
// This doesn't cause ArrangeOverride below to be called
element.InvalidateArrange();
}
Но вы действительно должны сделать панель недействительной, изменив свой код так:
public static void OnTopPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
UIElement panel= VisualTreeHelper.GetParent(source) as UIElement;
if(panel != null)
panel.InvalidateArrange();
}
И это должно работать (сделал для меня).
Если только InvalidateArrange не работает, вы также можете попробовать InvalidateMeasure или UpdateLayout.
У меня была эта проблема с дочерним контролем, который зависел от FontSize
родительского контроля. Я решил проблему, путешествуя по стопам родителей и лишая законной силы все:
static MyControl()
{
// replace base implementation of the dependent property
FontSizeProperty.OverrideMetadata(typeof(Scalar),
new FrameworkPropertyMetadata(SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits, OnMeasureInvalidated));
}
private static void OnMeasureInvalidated(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// recurse over parent stack
while (true)
{
var parent = VisualTreeHelper.GetParent(sender) as UIElement;
if (parent == null) return; // break on root element
parent.InvalidateMeasure();
sender = parent;
}
}