UserControl как интерфейс, но видимый в Разработчике

@Хуссейн Абдалла

Спасибо! Я закончил макет, который хочу. Но я все еще чувствую долгий путь.

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('tabbar demo...'),
      elevation: 0.0,
    ),
    body: Stack(children: [
      Row(
        children: <Widget>[
          Expanded(
            child: TabBar(
              labelColor: Colors.red[300],
              unselectedLabelColor: Colors.black38,
              indicator: BoxDecoration(),
              controller: _controller,
              isScrollable: true,
              tabs: <Widget>[
                Tab(text: 'cate_1'),
                Tab(text: 'cate_2'),
                Tab(text: 'cate_3'),                            
                Tab(text: 'cate_4'),
                Tab(text: 'cate_5'),
                Tab(text: 'cate_6'),
                Tab(text: 'cate_7'),
                Tab(text: 'cate_8'),
                Tab(text: 'cate_9'),
              ],
            ),
          ),
          Container(
            width: 40.0, 
            child: Icon(Icons.more_horiz, color: Colors.black26,),
          )
        ],
      ),
      Container(
        margin: EdgeInsets.only(top: 46.0),
        child: Text('xxxxxxxxxx'),
      )
    ]),
  );
}
7
задан Ryan Lundy 29 April 2009 в 21:37
поделиться

8 ответов

After you add the UserControl using the designer, you can set GenerateMember to false in the Properties window to suppress generation of a member.

You could then use some other technique in the constructor to assign your cName reference, e.g.:

foreach(Control control in this.Controls)
{
    cName = control as ICustomerName;
    if (cName != null) break;
}

cName would then be the only reference to the UserControl.

3
ответ дан 6 December 2019 в 11:52
поделиться

Если SomeCustomerNameUserControl определен следующим образом:

class SomeCustomerNameUserControl : UserControl, ICustomerName
{
}

Вы по-прежнему можете удалить этот элемент управления в конструкторе (который создает someCustomerNameUserControl1) и делать это всякий раз, когда вам необходимо: [12153 Может быть, я что-то упускаю, но я думаю, что это так просто.

6
ответ дан 6 December 2019 в 11:52
поделиться

you could as well do as Bob said but assign all your member variables in the constructor, then you have it in one place.

0
ответ дан 6 December 2019 в 11:52
поделиться

Похоже, вы хотите реализовать шаблон-посредник. Вместо того, чтобы иметь дело с каждым из пользовательских контроллеров bazillion напрямую, вы будете взаимодействовать с ними через посредника. Каждый посредник будет определять тонкий интерфейс, который вы хотите видеть из каждого элемента управления. Это уменьшит общую сложность, сделав ваш дизайн более четким и лаконичным. Например, вам не понадобятся 20 свойств и 50 методов, доступных на одном из ваших элементов управления. Вместо этого вы должны иметь дело с посредником для этого элемента управления, который определяет 2 свойства и 5 методов, которые вам действительно нужны. Все будет отображаться в конструкторе, но другие части вашего приложения не будут взаимодействовать с этими элементами управления - они будут взаимодействовать с посредниками.

Одним из больших преимуществ этого подхода является то, что он значительно упрощает обслуживание. Если вы решите, что MyCrappyUserControl необходимо переписать из-за плохой реализации, вам просто нужно обновить класс-посредник для этого элемента управления. Все остальные классы, которые взаимодействуют с элементом управления, делают это через посредника и остаются неизменными.

В конечном счете, все сводится к дисциплине: вы и ваша команда должны быть достаточно дисциплинированными, чтобы использовать посредники / интерфейсы / что угодно вместо непосредственного использования. ударяя управления. Приступите к проверке кода через плечо ведущим программистом, если ваша команда находится в нижней части шкалы дисциплины.

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

В конечном счете, все сводится к дисциплине: вы и ваша команда должны быть достаточно дисциплинированными, чтобы использовать посредники / интерфейсы / что угодно вместо непосредственного использования. ударяя управления. Приступите к проверке кода через плечо ведущим программистом, если ваша команда находится в нижней части шкалы дисциплины.

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

В конечном счете, все сводится к дисциплине: вы и ваша команда должны быть достаточно дисциплинированными, чтобы использовать посредники / интерфейсы / что угодно вместо непосредственного использования. ударяя управления. Приступите к проверке кода через плечо ведущим программистом, если ваша команда находится в нижней части шкалы дисциплины.

0
ответ дан 6 December 2019 в 11:52
поделиться

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

Это то, что делает Windows Forms, когда, например, свойство базового класса ничего не значит для конкретного потомок. Например, Control имеет свойство Text, но свойство Text не имеет смысла, скажем, в TabControl. Поэтому TabControl переопределяет свойство Text и добавляет атрибуты к его переопределению, говоря: «Кстати, не отображайте мое свойство Text в таблице свойств или в Intellisense». Это свойство все еще существует, но поскольку вы его никогда не видите, оно не мешает вам.

Если вы добавите атрибут [EditorBrowsable (EditorBrowsableState.Never)] к члену (свойству или методу) ), то Intellisense больше не будет отображать этот элемент в своих списках завершения кода. Если я правильно понимаю ваш вопрос, это то, чего вы пытаетесь достичь: усложнить для кода приложения случайное использование элемента.

Для свойств вы, вероятно, также захотите добавить [ Browserable (false)] , чтобы скрыть свойство из таблицы свойств, и [DesignerSerializationVisibility (DesignerSerializationVisibility. Скрыто)] , чтобы конструктор не мог записать значение свойства в файл .designer.cs.

Это очень затруднит случайное использование метода / свойства. Они все еще не гарантия, хотя. Если вам нужна гарантия, добавьте также атрибут [Устаревший] и выполните сборку с «Обрабатывать предупреждения как ошибки» - тогда вы позаботитесь о.

Если базовый член Виртуальный, вы, вероятно, хотите переопределить его, и пусть ваше переопределение просто вызывает base. Не выбрасывайте исключение, поскольку переопределенный член, вероятно, будет вызываться базовым классом во время обычного хода событий. С другой стороны, если элемент base не является виртуальным, тогда вы хотите использовать «new» вместо «override», и вы можете решить, должна ли ваша реализация вызывать base, или просто выбросить исключение - никто не должен использовать ваш повторно введенный член в любом случае, так что это не должно иметь значения.

public class Widget : UserControl
{
    // The Text property is virtual in the base Control class.
    // Override and call base.
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Obsolete("The Text property does not apply to the Widget class.")]
    public override string Text
    {
        get { return base.Text; }
        set { base.Text = value; }
    }

    // The CanFocus property is non-virtual in the base Control class.
    // Reintroduce with new, and throw if anyone dares to call it.
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Obsolete("The CanFocus property does not apply to the Widget class.")]
    public new bool CanFocus
    {
        get { throw new NotSupportedException(); }
    }

    // The Hide method is non-virtual in the base Control class.
    // Note that Browsable and DesignerSerializationVisibility are
    // not needed for methods, only properties.
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Obsolete("The Hide method does not apply to the Widget class.")]
    public new void Hide()
    {
        throw new NotSupportedException();
    }
}

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

6
ответ дан 6 December 2019 в 11:52
поделиться

'I want ICustomerName to be the only option for accessing the UserControl's variable. The idea is that a developer doesn't have to "just remember" to cast it.'

The problem you are having is that you have two completely divergent uses for your form and the controls it hosts. There is no trick built into Visual Studio or winforms which solves this neatly for you. It may be possible, but there is a much cleaner and object oriented way to separate the two methods of interacting with the controls.

If you want to hide the fact that these objects inherit from UserControl, and just want to treat them as IDoSomeThingYouShouldDealWith, you need to separate the logic that deals with the presentation concerns (designer + UI logic) from your business logic.

Your form class, should rightly deal with the controls as UserControls, docking, anchoring etc etc, nothing special here. You should put all the logic that needs to deal with ICustomerName.FirstName = etc into a completely separate class. This class doesn't care or know about fonts and layout, it just knows that there is another instance that can present a customer name; or a DateTime as a 'date of birth choosing' control properly etc.

This is a really lame example, but I have to go right now. You should be able to get the idea covered here in more detail:

public interface ICustomerName
    {
        void ShowName(string theName);
    }

    public partial class Form1 : Form, ICustomerName
    {
        public Form1()
        {
            InitializeComponent();
        }

        #region ICustomerName Members

        public void ShowName(string theName)
        {
            //Gets all controls that show customer names and sets the Text propert  
            //totheName
        }

        #endregion
    }

    //developers program logic into this class 
    public class Form1Controller
    {
        public Form1Controller(ICustomerName theForm) //only sees ICustomerName methods
        {
            //Look, i can't even see the Form object from here
            theForm.ShowName("Amazing Name");
        }
    }
4
ответ дан 6 December 2019 в 11:52
поделиться

You could write an extension method that would allow you to return any controls on the form that implement an interface.

public static class FormExtensions
{
    public static IDictionary<string, T> GetControlsOf<T>(this Form form) 
           where T: class
    {
        var result = new Dictionary<string, T>();
        foreach (var control in form.Controls)
        {
            if ((control as T) != null)
                result.Add((control as T).Tag, control as T);
        }
        return result;
    }
}

Then in your form you could call it whereever you want by:

this.GetControlsOf<ICustomerName>()["NameOfControlHere"];

In the event that it returns more than one user control you would need to handle that some how, perhaps by adding Tag property to the interface to uniquely keep track of each user control or something, like so

public partial class UserControl1 : UserControl, ICustomerName
{
     public string Tag { get { return this.Name; } }
}

You can then drag and drop the user controls onto your form from the designer. Tag will always return the name of your control, which will allow you to directly access the control through the IDictionary's interface. You're developers could put whatever unique identifier they want in the name of the control, and it would carry through to the interface.

Also, it should be noted that this approach will ALSO allow you to use this on ALL forms in your solution.

The only other thing you would need to do is set your GenerateMember to false.

1
ответ дан 6 December 2019 в 11:52
поделиться

Assume that MyUserControl is defined like this:

class MyUserControl : UserControl, IMyInterface
{
    // ...
}

Then in your form, you should have something like this:

public class MyForm : Form
{
    IMyInterface cName;

    public MyForm()
    {
        InitializeComponent();

        cName = new MyUserControl();
        Controls.Add((UserControl)cName);
    }
}

This way, cName is the only way to access this instance of our usercontrol.

-3
ответ дан 6 December 2019 в 11:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: