Автоматическое приведение, C#

#c# #silverlight

#c# #silverlight

Вопрос:

Пожалуйста, проанализируйте этот код.. Я подумал, что мы можем объединить метку и элемент управления в один общий элемент управления. Я реализую таким образом:

 //my class
public partial class WrappingControl : UserControl
{
    public string MyLabel { get { return lb.Text; } set { lb.Text = value; } }

    public UIElement MyControl { get { return uc.Content; } set { uc.Content = value; } }

    public T CastControl<T>()
    {
        return (T)Convert.ChangeType(MyControl, typeof(T), null);
    }
}

//silverlight class
public partial class WrappingControl : UserControl
{
    TextBlock lb;
    UserControl uc;
}
  

и я могу использовать, с чем-то вроде этого:

 var wc =new WrappingControl();
        wc.MyLabel = "ID:";
        wc.MyControl = new TextBox();
        wc.CastControl<TextBox>().Text="123";


        var wc2 = new WrappingControl();
        wc2.MyLabel = "Is Ok?:";
        wc2.MyControl = new CheckBox();
        wc2.CastControl<CheckBox>().IsChecked = true;
  

Единственным недостатком является то, что я вынужден повторять, какой тип элемента управления я хочу, но мне интересно, есть ли способ (возможно, путем отражения) определить тип элемента управления и вернуть объект этого типа и получить для этого intellisense. Нравится:

 wc.ActualControl.Text = "123"; 
wc2.ActualControl.IsChecked = true;
  

Ответ №1:

Если вы используете C # 4 и .NET 4, вы могли бы просто использовать динамическую типизацию. Измените свой тип свойства на dynamic , и вы ушли:

 wc.WrappedControl = new CheckBox();
wc.WrappedControl.IsChecked = true;
  

Обратите внимание, что вы не можете определить тип времени выполнения элемента управления и ожидать, что это повлияет на типы времени компиляции.

Рассматривали ли вы возможность использования дженериков?

 public partial class WrappingControl<T> : UserControl where T : UIElement
{
    public T WrappedControl { get; set; } // Add appropriate logic
}

var wc2 = new WrappingControl<CheckBox>();
wc2.WrappedControl = new CheckBox();
wc2.WrappedControl.IsChecked = true;
  

Если вы также зададите T new() ограничение, WrappingControl можно даже создать вложенный элемент управления.

Другой альтернативой является использование инициализатора объекта, чтобы избежать необходимости часто ссылаться на элемент управления:

 var wc = new WrappedControl
{
    MyLabel = "Test",
    MyControl = new CheckBox { IsChecked = true }
};
  

Комментарии:

1. XAML для Silverlight не обрабатывает общие элементы управления, если они используются в разметке XAML. Конечно, XAML 2009 поддерживает дженерики, но только в виде отдельных документов. Если это только для кода, то дженерики, безусловно, будут работать.

2. @vcsjones: Похоже, что это используется из code-behind больше, чем XAML, но ваша точка зрения принята.

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

Ответ №2:

Я думаю, Джон дал вам ответ. Однако вы можете рассмотреть возможность использования шаблонного элемента управления, основанного на ContentControl , а не на UserControl . Вот начало для 10.

Добавьте новый элемент управления с шаблоном Silverlight в свой проект, назовите его «LabeledControl».

Измените файл .cs create так, чтобы он выглядел следующим образом:-

 public class LabeledControl : ContentControl
{
    public LabeledControl()
    {
        this.DefaultStyleKey = typeof(LabeledControl);
    }

    #region public string Label
    public string Label
    {
        get { return GetValue(LabelProperty) as string; }
        set { SetValue(LabelProperty, value); }
    }


    public static readonly DependencyProperty LabelProperty =
        DependencyProperty.Register(
            "Label",
            typeof(string),
            typeof(LabeledControl),
            new PropertyMetadata(null));
    #endregion public string Label
}
  

Вы заметите, что все, что мы сделали, это изменили наследуемый класс ContentControl и добавили свойство зависимости «Label».

Теперь откройте файл Themes / Generic.xaml и найдите стиль по умолчанию для этого элемента управления. Замените его этим:-

 <Style TargetType="local:LabeledControl">
    <Setter Property="Foreground" Value="#FF000000"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Top"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:LabeledControl">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock Text="{TemplateBinding Label}" />
                    <ContentPresenter
                            Content="{TemplateBinding Content}"
                            ContentTemplate="{TemplateBinding ContentTemplate}"
                            Cursor="{TemplateBinding Cursor}"
                            Margin="{TemplateBinding Padding}"
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
  

Все, что мы здесь сделали, это скопировали исходный стиль для ContentControl и завернули его в a Grid , чтобы мы могли поместить a TextBlock , который мы привязываем к Label свойству.

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

Что касается вашей конкретной проблемы, я бы сказал, что на самом деле это не проблема, в последнем примере Джона он показывает, как вы можете создать свой код, чтобы избежать дублирования. С приведенным выше элементом управления этот код будет выглядеть следующим образом:-

 var lc = new LabeledControl
{
    Label = "Check this:",
    Content = new CheckBox {IsChecked = True}

};
  

ИЛИ в Xaml:-

 <local:LabeledControl Label="Check this:">
    <CheckBox IsChecked="True" />
</local>
  

Как указывает vcsjones Джону, использование универсального класса — не очень дружественный к Silverlight подход, я бы этого избегал.