#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));
};