Myślisz, że zarabiasz tyle, na ile zasługujesz? Zapraszamy do wzięcia udziału w anonimowej ankiecie.
1

Witam,

Potrzebuję metodę która będzie zwracała listę wzorców kontrolek wg których te kontrolki mają się tworzyć. Metoda ta będzie przeładowana w klasie pochodnej przez co każda klasa pochodna może definiować inną listę kontrolek.

Pisząc wzorzec mam na myśli jak to ma być kontrolka i jak ma mieć ustawione właściwości.

Poniżej rozpiszę o co mniej więcej chodzi. Teraz np mam taką metodę:


public static Control TworzKontrolke<TypKontrolki>(Action<TypKontrolki> akcja)
            where TypKontrolki : Control, new()
{
    TypKontrolki kontrolka = new TypKontrolki();
    akcja.Invoke(kontrolka);
    return kontrolka;
}

przez co można napisać w kodzie np formatki:


this.Controls.Add(Metody.TworzKontrolke<Dane, TextBox>(
a => {
        a.Text = "QWE ASD ZXC";
        a.Width = 100;
        a.Height = 100;
        a.Multiline = true;
        a.Location = new Point(10, 10);
}));

Docelowo powinno być tak:


public abstract class DaneGeneratora
{
    public abstract Type Typ { get; }
    public abstract Action<???> Akcja { get; }
}

public class DaneGeneratora<TypKontrolki>
    where TypKontrolki : Control, new()
{
    public override Type Typ
    {
        get
        {
            return typeof(TypKontrolki);
        }
    }
    private Action<TypKontrolki> _akcja;
    public override Action<TypKontrolki> Akcja
    {
        get
        {
            return akcja;
        }
    }

    public DaneGeneratora(Action<TypKontrolki> akcja)
    {
        _akcja = akcja;
    }
}

potem mógłbym to tak zastosować:


public void TworzKontrolki(List<DaneGeneratora> dane)
{
    foreach(var d in dane)
    {
        Control k = (Control)Activator.CreateInstance(dane.Typ);
        dane.Akcja.Invoke(k);
        this.Controls.Add(k);
    }
}

Niestety problem polega na tym co jest w klasie bazowej DaneGeneratora. Nie wiem jak w klasie DaneGeneratora zrobić "schowek" na akcje które mają być wykonane na kontrolce. Nie mogę wstawić tam Action bo nie można rzutować Action<> na Action bo to delegaty. Próbowałem wrzucić tam typ Delegate ale potem nie wiem jak to uruchomić na kontrolce. Macie jakieś pomysły jak to zrobić?

Pozdrawiam

flag

2 Answers

2

Spróbuj tak:

class ControlFactory
{
    Dictionary<Type, object> configuration = new Dictionary<Type,object>();

    public T Build<T>()
    {
        var c = Activator.CreateInstance<T>();
        var a = (Action<T>)configuration[typeof(T)];
        a.Invoke(c);
        return c;
    }

    public void Add<T>(Action<T> configureAction)
    {
        configuration[typeof(T)] = configureAction;
    }
}

Wiem, że ten object jest lekko obleśny. Niestety nie można znaleźć tutaj wspólnego mianownika, ale można się dość dobrze zabezpieczyć przed bałaganem (zrzucić to na fabrykę). Użycie jest proste:

var cf = new ControlFactory();
cf.Add<Form>(x => { x.Text = "foo"; });
cf.Add<Button>(x => { x.Text = "bar"; });

var f = cf.Build<Form>();
var b = cf.Build<Button>();

EDIT:

Oczywiście możesz wzbogacić fabrykę o dodatkową logikę, zmieniając typ klucza (string zamiast typu). Można dodać kilka nazwanych szablonów. Jeśli chcesz enumerować po jakiejś liście kontrolek do stworzenia - wydaje mi się, że najlepiej będzie użyć dwóch klas - jednej do przeglądania tej listy (zwraca kolejne nazwy szablonów) i z fabryki (buduje kontrolkę na podstawie szablonu).

W ogóle wydaje mi się, że najlepszym sposobem było by tu użycie Reflection i zasysanie danych kontrolek z zewnątrz (np.: YAML, XML, CSV, TXT). Dzięki temu nie wiążesz ich bezpośrednio do kodu. Reflection nie jest takie złe jak się wydaje, trzeba tylko wiedzieć gdzie je użyć.

link|flag
hehe no to mnie wyprzedziles wczoraj wieczorem tez mi przyszedl do glowy wlasnie taki pomysl i chcialem rano go zamiescic dzieki wielkie i pozdrawiam – bodziec Mar 6 at 9:24
hmm jak sie dobrze przyjrzałem to jednak nie do końca o to mi chodziło zaraz spróbuję napiasć dokładniej – bodziec Mar 6 at 11:03
0

Pomysł Marcina Seredyńskiego prawie pasuje. Faktycznie tworzenie kontrolki wypada wyrzucić na poziom ControlFactory i wtedy wszystko jest proste. Na ratunek przyszedł interfejs:


public interface IControlFactory
{
    Control CreateControl();
}

public sealed class ControlFactory<ControlType> : IControlFactory
    where ControlType : Control, new()
{
    private Action<ControlType> _action;

    public ControlFactory(Action<ControlType> action)
    {
        _action = action;
    }

    #region IControlFactory Members

    public Control CreateControl()
    {
        ControlType control = new ControlType();
        _action.Invoke(control);
        return control;
    }

    #endregion
}

teraz możemy zrobić coś takiego:


public abstract class KlasaBazowa
{
    public abstract IEnumerable<IControlFactory> ListaKontrolek
    {
        get;
    }
}

public class Klasa : KlasaBazowa
{
    public override IEnumerable<IControlFactory> ListaKontrolek
    {
        get
        {
            var k = new List<IControlFactory>();

            k.Add(new ControlFactory<Data, TextBox>(
                x => x.Value,
            a =>
            {
                a.Text = "ASD";
                a.Size = new Size(100, 50);
                a.Location = new Point(10, 10);
                a.Multiline = true;
            }));

            k.Add(new ControlFactory<Data, Button>(
            x => x.Value,
            a =>
            {
                    a.Text = "Przycisk";
                a.Size = new Size(100, 50);
                a.Location = new Point(10, 70);
            }));

        return k;
        }
    }
}

oraz użycie na formatce:


Klasa klasa = new Klasa();

foreach (var icf in klasa.ListaKontrolek) this.Controls.Add(icf.CreateControl());

link|flag
Taka jedna uwaga - czy nie lepiej mieć jedną instancję fabryki, zamiast specjalizowane instancje do każdego typu tworzonej kontrolki? Moim zdaniem wyabstrahowanie tego przyniesie same korzyści. – vigrid Mar 6 at 11:50

Your Answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.