Как я могу добавить объект шаблона в шаблон фрейма C #

#c# #xaml #xamarin #xamarin.forms #contentview

#c# #xaml #xamarin #xamarin.forms #contentview

Вопрос:

Вот код, над которым я работал с помощью Deczalof:

Вот код XAML, который у меня есть

 <t:PopupEntryFrame2 x:Name="newDeckNameEntry" TextChanged="newDeckNameEntry_TextChanged" />
  

С помощью кода:

 public partial class CopyDeckPopup : Rg.Plugins.Popup.Pages.PopupPage
{
    string originalName;
    string originalDescription;
    List<Deck> listDecks;

    public CopyDeckPopup(string clickedDeckName, string clickedDeckDescription)
    {
        InitializeComponent();
        listDecks = App.DB.GetAllDecks();
        newDeckNameEntry.Text = clickedDeckName;
        newDeckDescriptionEntry.Text = clickedDeckDescription;
        originalName = clickedDeckName;
        originalDescription = clickedDeckDescription;
        OK_Button.IsEnabled = false;
    }

    private async void Cancel_Button_Clicked(object sender, EventArgs e)
    {
        await PopupNavigation.Instance.PopAsync(false);
    }

    private async void OK_Button_Clicked(object sender, EventArgs e)
    {
        if (IsBusy)
            return;
        IsBusy = true;
        await PopupNavigation.Instance.PopAsync(false);
        var newDeckNameEntryTextTrim = newDeckNameEntry.Text.Trim();
        var newDeckDescriptionEntryTextTrim = newDeckDescriptionEntry.Text.Trim();
        if (newDeckNameEntryTextTrim != originalName || newDeckDescriptionEntryTextTrim != originalDescription)
        {
            App.DB.CopyDeckToDb2(originalName, newDeckNameEntryTextTrim, newDeckDescriptionEntryTextTrim);
            MessagingCenter.Send<PopupPage>(new PopupPage(), "PageRefresh");
        }
        IsBusy = false;
    }

    void newDeckNameEntry_TextChanged(object sender, EntryTextChangedEventArgs e)
    {
        NewDeckNameEntryValidator(e.NewTextValue);
    }

    void newDeckDescriptionEntry_TextChanged(object sender, EntryTextChangedEventArgs e)
    {
        var deckName = newDeckNameEntry.Text.Trim();
        var isDeckAvailable = listDecks.Where(x => x.Name == deckName).SingleOrDefault();
        if (isDeckAvailable == null)
        {
            OK_Button.IsEnabled = e.NewTextValue != originalDescription ? true : false;
        }
    }

    void NewDeckNameEntryValidator(string newDeckNameEntry)
    {
        var newDeckNameEntryTrimmed = newDeckNameEntry.Trim();
        var isDeckNameAvailable = listDecks.Where(x => x.Name == newDeckNameEntryTrimmed).SingleOrDefault();
        if (string.IsNullOrWhiteSpace(newDeckNameEntryTrimmed) ||
            isDeckNameAvailable != null ||
            newDeckNameEntryTrimmed.StartsWith("::") ||
            newDeckNameEntryTrimmed.EndsWith("::") ||
            newDeckNameEntryTrimmed.Count(c => c == ':') > 2)
        {
            OK_Button.IsEnabled = false;
            return;
        }
        OK_Button.IsEnabled = true;
    }

}
  

и код C # для шаблона:

 public class PopupEntryFrame2 : CustomFrame
{

    CustomEntry entry { get; set; }

    public PopupEntryFrame2()
    {

        entry = new CustomEntry();
        entry.SetBinding(PopupEntryFrame2.TextProperty, new Binding("Text", source: this));

        entry.TextChanged  = (s, a) =>
        {
            OnTextChanged(new EntryTextChangedEventArgs(a.NewTextValue, a.OldTextValue));
        };

        Content = entry;

        CornerRadius = 5;
        HasShadow = false;
        SetDynamicResource(BackgroundColorProperty, "EntryFrameBackgroundColor");
        SetDynamicResource(BorderColorProperty, "EntryFrameBorderColor");
        SetDynamicResource(CornerRadiusProperty, "EntryFrameCornerRadius");
        SetDynamicResource(HeightRequestProperty, "PopupEntryFrameHeight");
        SetDynamicResource(MarginProperty, "PopupEntryFrameMargin");
        SetDynamicResource(PaddingProperty, "PopupEntryFramePadding");
    }

    public class EntryTextChangedEventArgs : EventArgs
    {

        public EntryTextChangedEventArgs(String newValue = null, String oldValue = null)
        {
            NewTextValue = newValue;
            OldTextValue = oldValue;
        }

        public String NewTextValue { get; }

        public String OldTextValue { get; }

    }

    public event EventHandler TextChanged;


    protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
    {
        TextChanged?.Invoke(this, args);
    }

    public static readonly BindableProperty TextProperty =
        BindableProperty.Create(nameof(Text), typeof(string), typeof(PopupEntryFrame2), default(string));

    public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }

}
  

Ошибка, которую я получаю при сборке, заключается в следующем:

 CopyDeckPopup.xaml(22,63): XamlC error XFC0002: EventHandler "newDeckNameEntry_TextChanged" 
with correct signature not found in type "DecksTab.Pages.DeckOptions.CopyDeckPopup"
  

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

1. Вы могли бы посмотреть на contentView , он может удовлетворить ваши потребности.

Ответ №1:

Для достижения своей цели вы можете просто добавить Entry на свой PopupEntryFrame class и определить Event там, который соединяется с TextChanged событием в оригинале Entry .

Это делается, как показано в приведенном ниже коде (который основан на вашем!)

 using Test.Renderers;

namespace Test.Templates
{
    public class PopupEntryFrame : CustomFrame
    {

        Entry entry { get; set; }

        public PopupEntryFrame()
        {

            entry = new Entry();

            entry.TextChanged  = (s, a) =>
            {
                OnTextChanged(new EntryTextChangedEventArgs());
            };

            Content = entry;

            CornerRadius = 5;
            HasShadow = false;
            SetDynamicResource(BackgroundColorProperty, "EntryFrameBackgroundColor");
            SetDynamicResource(BorderColorProperty, "EntryFrameBorderColor");
            SetDynamicResource(CornerRadiusProperty, "EntryFrameCornerRadius");
            SetDynamicResource(HeightRequestProperty, "PopupEntryFrameHeight");
            SetDynamicResource(MarginProperty, "PopupEntryFrameMargin");
            SetDynamicResource(PaddingProperty, "PopupEntryFramePadding");
        }

        public class EntryTextChangedEventArgs : EventArgs
        {
            // class members  
        }

        public event EventHandler TextChanged;
        protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
        {
            TextChanged?.Invoke(this, args);
        }

    }
}
  

И это все. Делая это, теперь вы можете писать код, подобный

 <t:PopupEntry x:Name="newDeckDescriptionEntry" TextChanged="newDeckDescriptionEntry_TextChanged">
  

Обновить

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

Отказ от ответственности

Прежде всего, важно знать, что Frame он наследует себя от ContentView (от которого, собственно, он наследует свое Content свойство!). Фактически, из документации мы знаем, что

 [Xamarin.Forms.ContentProperty("Content")]
[Xamarin.Forms.RenderWith(typeof(Xamarin.Forms.Platform._FrameRenderer))]
public class Frame : Xamarin.Forms.ContentView, Xamarin.Forms.IBorderElement, Xamarin.Forms.IElementConfiguration<Xamarin.Forms.Frame>
  

это означает, что создание класса / элемента управления, который наследуется от Frame , означает, что мы уже используем этот ContentView подход.

Создайте contentView

Прежде всего, мы создаем ContentView и устанавливаем для его содержимого значение a new PopupFrame() , которое само содержит an Entry , следующим образом

 public class PopupEntry : ContentView
{

    Entry entry { get; set; }

    public PopupEntry()
    {

        entry = new Entry();

        Content = new PopupFrame()
        {
            Content = entry
        };

    }

}
  

Добавить событие

Далее, как того требует OP, мы определяем Event для нашего ContentView , который будет срабатывать Text при Entry изменении. Следуя документации, этого можно добиться, добавив следующий фрагмент кода:

 public class EntryTextChangedEventArgs : EventArgs
{
    // class members  
}

public event EventHandler TextChanged;
protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
{
    TextChanged?.Invoke(this, args);
}
  

Теперь мы можем «связать» исходное TextChanged событие из элемента Entry управления с новым Event нашим ContentView , следующим образом:

 entry.TextChanged  = (s, a) =>
{
    OnTextChanged(new EntryTextChangedEventArgs());
};
  

Тогда наш код contentView будет выглядеть так

 public class PopupEntry : ContentView
{

    Entry entry { get; set; }

    public PopupEntry()
    {

        entry = new Entry();

        entry.TextChanged  = (s, a) =>
        {
            OnTextChanged(new EntryTextChangedEventArgs());
        };

        Content = new PopupFrame()
        {
            Content = entry
        };


    }

    public class EntryTextChangedEventArgs : EventArgs
    {
        // class members  
    }

    public event EventHandler TextChanged;
    protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
    {
        TextChanged?.Invoke(this, args);
    }

}
  

Завершение

Теперь, когда это ContentView определено, мы можем писать код, подобный

 <t:PopupEntry x:Name="newDeckDescriptionEntry" TextChanged="newDeckDescriptionEntry_TextChanged"/>
  

И это все! Надеюсь, это было полезно.

Счастливого кодирования!

PS:

Небольшое замечание об объявлении события: поскольку EntryTextChangedEventArgs является копией исходного TextChangedEventArgs, мы можем определить класс EntryTextChangedEventArgs следующим образом

 public class EntryTextChangedEventArgs : EventArgs
{

    public EntryTextChangedEventArgs(String newValue = null, String oldValue = null)
    {
        NewTextValue = newValue;
        OldTextValue = oldValue;
    }

    public String NewTextValue { get; }

    public String OldTextValue { get; }

}
  

а затем при создании экземпляра этого класса мы просто передаем ему напрямую значения из TextChangedEventArgs следующим образом

 entry = new Entry();

entry.TextChanged  = (s, a) =>
{
    OnTextChanged(new EntryTextChangedEventArgs(a.NewTextValue, a.OldTextValue));
};