如何将模板对象添加到 C# Frame 模板中
How can I add in a template object into a C# Frame template
这是我在 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"
要实现您的目标,您只需在 PopupEntryFrame
class
上添加 Entry
并在其中定义一个 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
属性!)。其实从the documentation我们知道
[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
的 Class/Control 意味着我们已经在使用 ContentView
方法。
创建内容视图
首先我们创建一个ContentView
并将其内容设置为一个new PopupFrame()
,它本身包含一个Entry
,如下
public class PopupEntry : ContentView
{
Entry entry { get; set; }
public PopupEntry()
{
entry = new Entry();
Content = new PopupFrame()
{
Content = entry
};
}
}
添加事件
接下来,根据 OP 的要求,我们为 ContentView
定义了一个 Event
,当 Entry
中的 Text
发生变化时将被触发。在 the Documentation 之后,可以通过添加以下代码来实现:
public class EntryTextChangedEventArgs : EventArgs
{
// class members
}
public event EventHandler TextChanged;
protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
{
TextChanged?.Invoke(this, args);
}
现在,我们可以将Entry
控件中原来的TextChanged
事件“link”到我们ContentView
的新Event
中,如下:
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"/>
就是这样!我希望这有用。
编码愉快!
P.S.:
关于事件声明的一点注意事项:由于 EntryTextChangedEventArgs 是原始 TextChangedEventArgs 的副本,我们可以像
一样定义 EntryTextChangedEventArgs class
public class EntryTextChangedEventArgs : EventArgs
{
public EntryTextChangedEventArgs(String newValue = null, String oldValue = null)
{
NewTextValue = newValue;
OldTextValue = oldValue;
}
public String NewTextValue { get; }
public String OldTextValue { get; }
}
然后在实例化这个 class 时,我们直接将 TextChangedEventArgs 的值提供给它,如下所示
entry = new Entry();
entry.TextChanged += (s, a) =>
{
OnTextChanged(new EntryTextChangedEventArgs(a.NewTextValue, a.OldTextValue));
};
这是我在 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"
要实现您的目标,您只需在 PopupEntryFrame
class
上添加 Entry
并在其中定义一个 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
属性!)。其实从the documentation我们知道
[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
的 Class/Control 意味着我们已经在使用 ContentView
方法。
创建内容视图
首先我们创建一个ContentView
并将其内容设置为一个new PopupFrame()
,它本身包含一个Entry
,如下
public class PopupEntry : ContentView
{
Entry entry { get; set; }
public PopupEntry()
{
entry = new Entry();
Content = new PopupFrame()
{
Content = entry
};
}
}
添加事件
接下来,根据 OP 的要求,我们为 ContentView
定义了一个 Event
,当 Entry
中的 Text
发生变化时将被触发。在 the Documentation 之后,可以通过添加以下代码来实现:
public class EntryTextChangedEventArgs : EventArgs
{
// class members
}
public event EventHandler TextChanged;
protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
{
TextChanged?.Invoke(this, args);
}
现在,我们可以将Entry
控件中原来的TextChanged
事件“link”到我们ContentView
的新Event
中,如下:
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"/>
就是这样!我希望这有用。
编码愉快!
P.S.:
关于事件声明的一点注意事项:由于 EntryTextChangedEventArgs 是原始 TextChangedEventArgs 的副本,我们可以像
一样定义 EntryTextChangedEventArgs classpublic class EntryTextChangedEventArgs : EventArgs
{
public EntryTextChangedEventArgs(String newValue = null, String oldValue = null)
{
NewTextValue = newValue;
OldTextValue = oldValue;
}
public String NewTextValue { get; }
public String OldTextValue { get; }
}
然后在实例化这个 class 时,我们直接将 TextChangedEventArgs 的值提供给它,如下所示
entry = new Entry();
entry.TextChanged += (s, a) =>
{
OnTextChanged(new EntryTextChangedEventArgs(a.NewTextValue, a.OldTextValue));
};