Подтвердить что ты не робот

Настройка настраиваемого свойства на странице WPF/Silverlight

Похоже, это должно быть просто. У меня есть Page, объявленный в XAML обычным способом (т.е. С "Добавить новый элемент..." ), и у него есть настраиваемое свойство. Я хотел бы установить это свойство в XAML, связанном со страницей.

Попытка сделать это так же, как я установил любое другое свойство, не работает, по причинам, которые я понимаю, но не знаю, как работать. Просто у нас есть что-то конкретное, о чем можно поговорить, здесь некоторые (недействительные) XAML. Я сократил все как можно больше - изначально были атрибуты, такие как размер дизайнера, но я считаю, что они не имеют отношения к тому, что я пытаюсь сделать.

<Page x:Class="WpfSandbox.TestPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      MyProperty="MyPropertyValue">
</Page>

и соответствующий код:

using System.Windows.Controls;

namespace WpfSandbox {
  public partial class TestPage : Page {
    public TestPage() {
      InitializeComponent();
    }

    public string MyProperty { get; set; }
  }
}

Сообщение об ошибке:

Ошибка 1 Свойство "MyProperty" не существует в пространстве имен XML 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'. Строка 4 Позиция 7.

Теперь я знаю, почему это происходит: элемент имеет тип Page, а Page не имеет свойства, называемого MyProperty. Это объявляется только в TestPage..., который указан атрибутом x:Class, но не самим элементом. Насколько мне известно, эта конфигурация требуется для модели обработки XAML (т.е. Интеграции Visual Studio и т.д.).

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

Вышеприведенный пример WPF, но я подозреваю, что те же ответы будут применяться в Silverlight. Я заинтересован в обоих - поэтому, если вы отправляете ответ, который, как вы знаете, будет работать в одном, но не в другом, я был бы признателен, если бы вы указали, что в ответ:)

Я собираюсь пинать себя, когда кто-то публикует абсолютно тривиальное решение...

4b9b3361

Ответ 1

Вы можете работать с обычным свойством без свойства Dependency, если вы создаете базовый класс для своей страницы.

 public class BaseWindow : Window
 {
   public string MyProperty { get; set; }
 }

<local:BaseWindow x:Class="BaseWindowSample.Window1" x:Name="winImp"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:BaseWindowSample" 
    MyProperty="myproperty value"
    Title="Window1" Height="300" Width="300">

</local:BaseWindow>

И он работает, хотя MyProperty не является зависимым или не прикрепленным.

Ответ 2

Вам нужно было бы сделать это прикрепляемым свойством, как отметил Павел, тогда вы можете написать что-то вроде этого

<Page x:Class="JonSkeetTest.SkeetPage"
      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:JonSkeetTest="clr-namespace:JonSkeetTest" mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
       JonSkeetTest:SkeetPage.MyProperty="testar"
    Title="SkeetPage">
    <Grid>

    </Grid>
</Page>

Однако только с этим кодом:

Вместо этого вы получите эту ошибку:

Прикрепляемое свойство MyProperty не было найдено в типе "SkeetPage".

Прилагаемое свойство "SkeetPage.MyProperty" не определен на странице или на одном из базовых классов.


Edit

К сожалению, вам нужно использовать свойства зависимостей, ее рабочий пример

Страница

<Page x:Class="JonSkeetTest.SkeetPage"
      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:JonSkeetTest="clr-namespace:JonSkeetTest" mc:Ignorable="d" 
      JonSkeetTest:SkeetPage.MyProperty="Testing.."
      d:DesignHeight="300" d:DesignWidth="300"
    Title="SkeetPage">

    <Grid>
        <Button Click="ButtonTest_Pressed"></Button>
    </Grid>
</Page>

Код

using System.Windows;
using System.Windows.Controls;

namespace JonSkeetTest
{
    public partial class SkeetPage
    {
        public SkeetPage()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
          "MyProperty",
          typeof(string),
          typeof(Page),
          new FrameworkPropertyMetadata(null,
              FrameworkPropertyMetadataOptions.AffectsRender
          )
        );

        public static void SetMyProperty(UIElement element, string value)
        {
            element.SetValue(MyPropertyProperty, value);
        }
        public static string GetMyProperty(UIElement element)
        {
            return element.GetValue(MyPropertyProperty).ToString();
        }

        public string MyProperty
        {
            get { return GetValue(MyPropertyProperty).ToString(); }
            set { SetValue(MyPropertyProperty, value); }
        }

        private void ButtonTest_Pressed(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(MyProperty);
        }
    }
}

Если вы нажмете кнопку, вы увидите "Тестирование..." в MessageBox.

Ответ 3

Вместо элемента <Page> вы можете объявить элемент <Page>:

<YourApp:TestPage 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  xmlns:YourApp="clr-namespace:YourApp"
  MyProperty="Hello">
</YourApp:TestPage>

Это сделало бы трюк, но вы потеряете InitializeComponent() и стандартную конструкцию. Тем не менее, режим разработки по-прежнему работает безупречно, но я не тестировал это широко.

UPDATE:. Скомпилируется и запускается, но на самом деле не устанавливается MyProperty. Вы также теряете возможность привязывать обработчики событий в XAML (хотя может быть способ восстановить то, о чем я не знаю).

ОБНОВЛЕНИЕ 2: Рабочий образец из @Fredrik Mörk, который устанавливает свойство, но не поддерживает привязки обработчиков событий в XAML:

Код позади:

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        protected override void OnActivated(EventArgs e)
        {
            this.Title = MyProperty;
        }      

        public string MyProperty { get; set; }
    }
}

XAML:

<WpfApplication1:MainWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:WpfApplication1="clr-namespace:WpfApplication1" 
    Title="MainWindow" 
    Height="350" 
    Width="525"
    MyProperty="My Property Value"> 
</WpfApplication1:MainWindow>

Ответ 4

Ваш XAML эквивалентен следующему:

<Page x:Class="SkeetProblem.TestPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.MyProperty>MyPropertyValue</Page.MyProperty> 
</Page>

Это, очевидно, незаконно. XAML файл загружается статическим методом LoadComponent класса Application, а ссылка говорит:

Загружает файл XAML, который находится в указанном едином идентификаторе ресурса (URI), и преобразует его в экземпляр объекта, который указан корневым элементом файла XAML.

Это означает, что вы можете устанавливать свойства только для типа, указанного корневым элементом. Таким образом, вам нужно подклассировать страницу и указать этот подкласс как корневой элемент XAML.

Ответ 5

Это сработало для меня

<Window x:Class="WpfSandbox.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfSandbox"        
    xmlns:src="clr-namespace:WpfSandbox" 
    Title="MainWindow" Height="350" Width="525"
    src:MainWindow.SuperClick="SuperClickEventHandler">
</Window>

Итак, это может сработать по оригинальному вопросу (не пытайтесь). Примечание xmlns: src.

<Page x:Class="WpfSandbox.TestPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:WpfSandbox"        
  xmlns:src="clr-namespace:WpfSandbox" 
  src:TestPage.MyProperty="MyPropertyValue">
</Page>

Ответ 6

Мое предложение было бы DependencyProperty со значением по умолчанию:

    public int MyProperty
    {
        get { return (int)GetValue(MyPropertyProperty); }
        set { SetValue(MyPropertyProperty, value); }
    }

    public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.Register("MyProperty", typeof(int), typeof(MyClass), 
               new PropertyMetadata(1337)); //<-- Default goes here

См. свойства элементов управления как то, что вы открываете для использования внешним миром.

Если вы хотите использовать свое собственное свойство, вы можете использовать привязки ElementName или RelativeSource.

О вещи с избытком, DependencyProperties идут рука об руку с DependencyObjects;)

Больше не требуется XAML, значение по умолчанию в PropertyMetadata сделает все остальное.

Если вы действительно хотите поместить его в XAML, идите на решение базового класса или запретите боги, введите свойство attachable, которое также можно использовать и для любого другого элемента управления.

Ответ 7

Ответ относится к Silverlight.

Нет простого очевидного способа использовать свойство plain так, как вы хотите, и на этом пути должен быть некоторый компромисс.

Не работает: -

Некоторые предлагают свойство зависимости. Это не сработает, его все еще остается публичной собственностью от Xaml POV. Прикрепленное свойство будет работать, но это сделает работу с ним в коде безобразной.

Закрыть, но без банана: -

Xaml и класс могут быть полностью разделены следующим образом: -

<local:PageWithProperty
           xmlns:local="clr-namespace:StackoverflowSpikes"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
    Message="Hello World"
    Loaded="PageWithProperty_Loaded"
    Title="Some Title"
           >
    <Grid x:Name="LayoutRoot">
        <TextBlock Text="{Binding Parent.Message, ElementName=LayoutRoot}" />
    </Grid>
</local:PageWithProperty>

Код: -

public class PageWithProperty : Page
{

        internal System.Windows.Controls.Grid LayoutRoot;

        private bool _contentLoaded;

        public void InitializeComponent()
        {
            if (_contentLoaded) {
                return;
            }
            _contentLoaded = true;
            System.Windows.Application.LoadComponent(this, new System.Uri("/StackoverflowSpikes;component/PageWithProperty.xaml", System.UriKind.Relative));
            this.LayoutRoot = ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
         }

    public PageWithProperty()
    {
        InitializeComponent();
    }

    void PageWithProperty_Loaded(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Hi");
    }
    public string Message {get; set; }

}

Однако вы теряете поддержку от дизайнера. Примечательно, что вам нужно будет создавать поля для хранения ссылок на именованные элементы и назначать их сами в своей реализации InitialiseComponent (IMO все эти автоматические поля для именованных элементов не всегда являются хорошей вещью). Кроме того, разработчик не будет динамически создавать код события (хотя, как ни странно, он не знает, как перейти к тому, который вы создали вручную), однако события, определенные в Xaml, будут подключены во время выполнения.

Лучший вариант ИМО: -

Лучший компромисс уже был опубликован abhishek, используйте базовый класс шайбы для хранения свойств. Минимальное усилие, максимальная совместимость.

Ответ 8

Я просто пытался сделать то же самое с некоторыми разными намерениями.

Фактический ответ на самом деле таков: вам нужно, чтобы WPF-соглашение для Set-методов было сделано правильно. Как указано здесь: http://msdn.microsoft.com/en-us/library/ms749011.aspx#custom вам нужно определить методы SetXxx и GetXxx, если вы собираетесь определить прикрепленное свойство с именем Xxx.

См. этот рабочий пример:

public class Lokalisierer : DependencyObject
{
    public Lokalisierer()
    {
    }

    public static readonly DependencyProperty LIdProperty = 
        DependencyProperty.RegisterAttached("LId", 
                                            typeof(string), 
                                            typeof(Lokalisierer),
                                            new FrameworkPropertyMetadata( 
                                                  null,
                                                     FrameworkPropertyMetadataOptions.AffectsRender | 
                                                     FrameworkPropertyMetadataOptions.AffectsMeasure,
                                                     new PropertyChangedCallback(OnLocIdChanged)));

    private static void OnLocIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
    // on startup youll be called here
    }

    public static void SetLId(UIElement element, string value)
    {
      element.SetValue(LIdProperty, value);
    }
    public static string GetLId(UIElement element)
    {
      return (string)element.GetValue(LIdProperty);
    }


    public string LId
    {
        get{    return (string)GetValue(LIdProperty);   }
        set{ SetValue(LIdProperty, value); }
    }
}

И часть WPF:

<Window x:Class="LokalisierungMitAP.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:LokalisierungMitAP"
Title="LokalisierungMitAP" Height="300" Width="300"
>
<StackPanel>
    <Label  me:Lokalisierer.LId="hhh">Label1</Label>
   </StackPanel>

BTW: вам также нужно унаследовать DependencyObject

Ответ 9

Вам нужно будет определить его свойство attachable для доступа к нему следующим образом.

Ответ 10

Вы можете установить свойство с помощью стиля:

<Page.Style>
    <Style TargetType="{x:Type wpfSandbox:TestPage}">
        <Setter Property="MyProperty" Value="This works" />
    </Style>
</Page.Style>

Но это работает только для свойств зависимости!

public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
    nameof(MyProperty), typeof(string), typeof(Page),
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

public string MyProperty
{
    get { return (string)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}