Сложность с DependencyProperties

У меня возникли проблемы со свойствами зависимостей в моем приложении Mango для Windows Phone. Вот два элемента управления, размер шрифта которых я хотел бы изменить динамически:

<TextBlock Text="{Binding}" FontSize="{Binding ElementName=ParagraphItems, Path=DataContext.TextScale}" />
<local:HyperlinkTextBlock Text="{Binding}" FontSize="{Binding ElementName=ParagraphItems, Path=DataContext.TextScale}"  />

TextBlock работает нормально, а HyperlinkTextBlock нет. HyperlinkTextBlock - это класс, который я сделал:

<UserControl
  <!-- ... -->
  >

    <RichTextBox x:Name="LayoutRoot" TextWrapping="Wrap" FontSize="{Binding FontSize}">
        <Paragraph x:Name="BaseParagraph" />
    </RichTextBox> 

</UserControl>

public partial class HyperlinkTextBlock : UserControl { /* ... */ }

Я не уверен, что мне нужно сделать в HyperlinkTextBlock, чтобы он мог получать значения FontSize, когда он объявлен в XAML. Я попытался привязать свойство в HyperlinkTextBlock.xaml и уведомить об изменении свойства в коде программной части:

    public new double FontSize
    {
        get
        {
            return base.FontSize;
        }
        set
        {
            base.FontSize = value;
            onPropChanged("FontSize");
        }
    }

(Это new, потому что UserControl уже имеет свойство FontSize — разве я не могу просто использовать его?)

Я также попытался создать совершенно новое свойство зависимостей:

    public static readonly new DependencyProperty FontSizeProperty = DependencyProperty.RegisterAttached(
        "FontSize",
        typeof(double),
        typeof(HyperlinkTextBlock),
        new PropertyMetadata(20, new PropertyChangedCallback(onFontSizeChanged)));

    public new double FontSize
    {
        get { return (double)GetValue(FontSizeProperty); }
        set { SetValue(FontSizeProperty, value); }
    }

    private static void onFontSizeChanged(DependencyObject dependObj, DependencyPropertyChangedEventArgs e)
    {
        ((HyperlinkTextBlock)dependObj).LayoutRoot.FontSize = (double)e.NewValue;
    }

Аналогично, это не сработало. Во время выполнения выдает ошибку:

System.ArgumentException was unhandled
  Message=Default value type does not match type of property.
  StackTrace:
       at System.Windows.DependencyProperty.Register(Boolean fIsAttachedDP, String name, Type propertyType, Type ownerType, PropertyMetadata propertyMetadata, Boolean readOnly)
       at System.Windows.DependencyProperty.RegisterAttached(String name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
       at MyApp.Views.HyperlinkTextBlock..cctor()
       at System.Reflection.RuntimeConstructorInfo.InternalInvoke(RuntimeConstructorInfo rtci, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess, StackCrawlMark& stackMark)
       at System.Reflection.RuntimeConstructorInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, StackCrawlMark& stackMark)
       at System.Reflection.ConstructorInfo.Invoke(Object[] parameters)
       at MS.Internal.TypeProxy.<>c__DisplayClass30.<GetCreateObjectDelegate>b__2a()
       at MS.Internal.TypeProxy.CreateInstance(UInt32 customTypeId)
       at MS.Internal.XamlManagedRuntimeRPInvokes.CreateInstance(XamlTypeToken inXamlType, XamlQualifiedObject& newObject)

Каков правильный способ сделать это?

Обновление:

Если я просто установлю FontSize непосредственно на HyperlinkTextBlock:

     <local:HyperlinkTextBlock Text="{Binding}" Margin="0,15" FontSize="33.0" />
     <local:HyperlinkTextBlock Text="{Binding}" Margin="0,15" FontSize="40" />

И удалите все, что касается FontSize, из самого HyperlinkTextBlock:

<RichTextBox x:Name="LayoutRoot" TextWrapping="Wrap">
    <Paragraph x:Name="BaseParagraph" />
</RichTextBox>

Тогда нет заметного эффекта от установки размера шрифта. (Два текстовых блока, объявленных выше, кажутся идентичными.)


person Nick Heiner    schedule 23.01.2012    source источник


Ответы (3)


Может быть, я чего-то не понимаю, но почему вы определяете FontSize как присоединенное свойство? Я бы использовал простое свойство зависимости и, чтобы избежать путаницы, дал бы ему имя, отличное от FontSize (например, в данном случае HyperlinkFontSize), поэтому я бы сделал что-то вроде этого:

public static readonly DependencyProperty HyperlinkFontSize = DependencyProperty.Register(
    "HyperlinkFontSize",
    typeof(double),
    typeof( HyperlinkTextBlock ),
    new PropertyMetadata( 20.0, onFontSizeChanged) ) );

(Обратите внимание, что вам не нужно указывать имя типа делегата при передаче обработчика событий.)

Затем выполните привязку следующим образом:

<local:HyperlinkTextBlock Text="{Binding}" HyperlinkFontSize="{Binding ElementName=ParagraphItems, Path=DataContext.TextScale}" />

И последнее: сначала вы пытались реализовать FontSize как обычное свойство (с уведомлением об изменении). Это никогда не должно работать, потому что цель привязки данных всегда должна быть свойством зависимости (хотя источником может быть любое свойство CLR, даже без уведомления об изменении), как указано здесь.


ОБНОВЛЕНИЕ. Другим подходом может быть привязка свойства RichTextbox.FontSize к свойству FontSize пользовательского элемента управления, например:

<UserControl x:Name="hyperlinkTextboxUserControl" ...>

    <RichTextBox x:Name="LayoutRoot" TextWrapping="Wrap" FontSize="{Binding FontSize, ElementName=hyperlinkTextboxUserControl}">
        <Paragraph x:Name="BaseParagraph" />
    </RichTextBox> 

</UserControl>

Таким образом, вам не нужно дополнительное свойство зависимостей, и вы можете просто установить FontSize в local:HyperlinkTextBlock, как вы это делали изначально.

person Mark Vincze    schedule 23.01.2012
comment
Это работает. Спасибо. (Могу ли я каким-то образом использовать унаследованную FontSize вместо определения моей собственной версии?) - person Nick Heiner; 24.01.2012
comment
Добавлен подход к ответу для достижения этого. - person Mark Vincze; 24.01.2012

Измените код свойства зависимости следующим образом:

public static readonly new DependencyProperty FontSizeProperty = DependencyProperty.RegisterAttached(
    "FontSize",
    typeof(double),
    typeof(HyperlinkTextBlock),
    new PropertyMetadata((double)20, new PropertyChangedCallback(onFontSizeChanged)));

Int не является двойным...

person Emond Erno    schedule 23.01.2012
comment
Возможный альтернативный синтаксис: 20.0 автоматически выводит тип double, а 20D явно указывает ваше намерение, что литерал 20 должен быть введен как таковой. - person Tom W; 24.01.2012
comment
@TomW - да, я выбрал этот синтаксис, потому что он делает ответ наиболее ясным. В реальной жизни я использую 20.0 - person Emond Erno; 24.01.2012

Если что-то не отличается между Silverlight 5 и Windows Phone Silverlight, значение FontSize должно быть автоматически унаследовано от его родителя. Вам не нужно создавать какие-либо свойства зависимостей, и вам не нужно делать какие-либо привязки внутри вашего UserControl. Я только что проверил следующее (хотя и на ПК), и это сработало:

<UserControl x:Class="Test.HyperlinkTextBlock"
      <!-- ... -->
    >    
        <RichTextBox x:Name="LayoutRoot" TextWrapping="Wrap">
            <Paragraph x:Name="BaseParagraph">HyperlinkTextBox text</Paragraph>
        </RichTextBox>  
</UserControl>

и

<UserControl.Resources>
    <system:Double x:Key="SomethingToBindTo">28.0</system:Double>
</UserControl.Resources>

<StackPanel Margin="20">
    <TextBlock  Text="TextBlock text" FontSize="{Binding Source={StaticResource SomethingToBindTo}}" />
    <my:HyperLinkTextBlock FontSize="{Binding Source={StaticResource SomethingToBindTo}}" />
</StackPanel>

Это был результат:

результат

Обновлять:

Обязательно удалите/закомментируйте добавленное вами свойство FontSize, иначе мой пример не будет работать.

Если это все еще не работает для вас, и поскольку Silverlight 4 и более ранние версии не имеют расширения разметки FindAncestor, возможно, назовите свой UserControl и используйте его для привязки RichTextBox к нему.

<UserControl x:Class="Test.HyperlinkTextBox" Name="UserControlRoot"
      <!-- ... -->
    >   
        <RichTextBox x:Name="LayoutRoot"
                     TextWrapping="Wrap"
                     FontSize="{Binding ElementName=UserControlRoot, Path=FontSize}">
            <Paragraph x:Name="BaseParagraph">Text</Paragraph>
        </RichTextBox>
</UserControl>
person ChimeraObscura    schedule 23.01.2012
comment
Windows Phone не работает с SL5, и это не работает для меня - обновлена ​​OP - person Nick Heiner; 24.01.2012