英文:
Updating of property values & bindings in .NET MAUI ContentView
问题
以下是您提供的文本的翻译部分:
"I'm attempting to try my hand at creating a card-based UI similar to Android's CardView layout using MAUI ContentView. The exemplary use case for such an element is the display of testing or scoring results, so I attempted to roll in some of the underlying logic directly into the new element - the user would simply have to supply an Attempted and a Possible set of integers, i.e. 92 and 100, from which it would display an "out-of" format of 92/100 as well as a percentage of 92.0%.
The issue is that I've tried a number of ways to do this, and none successfully update the Score and Percentage properties correctly. I realize that it may be an issue with the order (or simultaneity) of the properties being set, but I haven't been able to rectify it using BindableProperty.Create(..., propertyChanged:) or other methods."
如果您需要更多帮助或有其他问题,请随时提出。
英文:
I'm attempting to try my hand at creating a card-based UI similar to Android's CardView
layout using MAUI ContentView
. The exemplary use case for such an element is the display of testing or scoring results, so I attempted to roll in some of the underlying logic directly into the new element - the user would simply have to supply an Attempted
and a Possible
set of integers, i.e. 92
and 100
, from which it would display an "out-of" format of 92/100
as well as a percentage of 92.0%
.
The issue is that I've tried a number of ways to do this, and none successfully update the Score
and Percentage
properties correctly. I realize that it may be an issue with the order (or simultaneity) of the properties being set, but I haven't been able to rectify it using BindableProperty.Create(..., propertyChanged:)
or other methods.
ProgressViewCard.xaml
:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView x:Name="this"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ExampleApp.Controls.ProgressCardView">
<Frame BindingContext="{x:Reference this}" BackgroundColor="{Binding BackgroundColor}" CornerRadius="5" HasShadow="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<!-- Configure the button functionality of the ProgressCardView -->
<Button x:Name="InternalButton" Grid.RowSpan="3" Grid.ColumnSpan="2" Opacity="0.0" Clicked="buttonEvent" />
<Label Text="{Binding Title}" HorizontalOptions="Start" VerticalOptions="Center" FontSize="17" TextColor="{Binding HeaderColor}" />
<Label Text="{Binding Score}" Grid.Column="1" Grid.ColumnSpan="2" HorizontalOptions="End" VerticalOptions="Center" FontSize="12" TextColor="{Binding TextColor}" />
<BoxView Grid.Row="1" Grid.ColumnSpan="3" HeightRequest="1" Color="{Binding TextColor}"/>
<ProgressBar x:Name="CardProgressBar" Grid.Row="2" Grid.ColumnSpan="2" />
<Label Text="{Binding Percentage}" Grid.Row="2" Grid.Column="2" HorizontalOptions="End" VerticalOptions="Center" FontSize="12" TextColor="{Binding TextColor}" />
</Grid>
</Frame>
</ContentView>
ProgressViewCard.xaml.cs
:
namespace ExampleApp.Controls
{
public partial class ProgressCardView : ContentView
{
private int attempted = 0;
private int possible = 0;
#region BindableProperties
public static readonly BindableProperty TitleProperty = BindableProperty.Create(
nameof(Title),
typeof(string),
typeof(ProgressCardView2),
string.Empty);
public static readonly BindableProperty AttemptedProperty = BindableProperty.Create(
nameof(Attempted),
typeof(int),
typeof(ProgressCardView2),
0);
public static readonly BindableProperty PossibleProperty = BindableProperty.Create(
nameof(Possible),
typeof(int),
typeof(ProgressCardView2),
1);
public static readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(
nameof(BackgroundColor),
typeof(Color),
typeof(ProgressCardView2),
Color.FromArgb("#FFFFFF"));
public static readonly BindableProperty HeaderColorProperty = BindableProperty.Create(
nameof(HeaderColor),
typeof(Color),
typeof(ProgressCardView2),
Color.FromArgb("#FFFFFF"));
public static readonly BindableProperty TextColorProperty = BindableProperty.Create(
nameof(TextColor),
typeof(Color),
typeof(ProgressCardView2),
Color.FromArgb("#FFFFFF"));
#endregion
#region Getters and Setters
public string Title
{
get => (string) GetValue(ProgressCardView2.TitleProperty);
set => SetValue(ProgressCardView2.TitleProperty, value);
}
public int Attempted
{
get => (int) GetValue(ProgressCardView2.AttemptedProperty);
set => SetValue(ProgressCardView2.AttemptedProperty, value);
}
public int Possible
{
get => (int)GetValue(ProgressCardView2.PossibleProperty);
set => SetValue(ProgressCardView2.PossibleProperty, value);
}
public string Score
{
get { return String.Format("{0}/{1}", this.attempted, this.possible); }
set { this.Score = value; }
}
public string Percentage
{
get { return String.Format("{0:P1}", ((double) this.attempted) / ((double) this.possible)); }
set { this.Score = value; }
}
public Color BackgroundColor
{
get => (Color) GetValue(ProgressCardView2.BackgroundColorProperty);
set => SetValue(ProgressCardView2.BackgroundColorProperty, value);
}
public Color HeaderColor
{
get => (Color) GetValue(ProgressCardView2.HeaderColorProperty);
set => SetValue(ProgressCardView2.HeaderColorProperty, value);
}
public Color TextColor
{
get => (Color) GetValue(ProgressCardView2.TextColorProperty);
set => SetValue(ProgressCardView2.TextColorProperty, value);
}
#endregion
#region Methods and Events
public ProgressCardView2()
{
InitializeComponent();
}
private void buttonEvent(object sender, EventArgs e)
{
}
#endregion
}
}
The usage of the controls is as follows:
<ctrls:ProgressCardView Title="CS 101 Final" Attempted="92" Possible="100" BackgroundColor="#ffffff"
HeaderColor="#e74c3c" TextColor="#7f8c8d" />
<ctrls:ProgressCardView Title="ME 302 Midterm" Attempted="68" Possible="85" BackgroundColor="#ffffff"
HeaderColor="#e74c3c" TextColor="#7f8c8d" />
This is the result in an Android emulator (API 31). How do I modify the above control to obtain the correct behavior?
答案1
得分: 3
你已经完成了大部分工作。我尝试了你的代码并进行了一些修改,这些修改对我有效。
首先,在自定义控件ProgressCardView中,删除前两行:
private int attempted = 0;
private int possible = 0;
因为这与BindableProperty无关,你可以在BindableProperty.Create中设置默认值,这将导致值始终为0。你可以在BindableProperty.Create中设置默认值。
public string Score
{
get { return String.Format("{0}/{1}", this.attempted, this.possible); }
}
其次,在BindableProperty中,你可以定义propertyChanged事件处理程序,用于在属性更改时定义回调方法。有关更多信息,你可以参考检测属性更改。
对于你的情况,AttemptedProperty和PossibleProperty的更改会导致结果发生更改,因此我们可以将propertyChanged添加到这两个BindableProperty中,如下所示:
public static readonly BindableProperty AttemptedProperty = BindableProperty.Create(
nameof(Attempted),
typeof(int),
typeof(ProgressViewCard),
0,
propertyChanged: OnThisPropertyChanged);
public static readonly BindableProperty PossibleProperty = BindableProperty.Create(
nameof(Possible),
typeof(int),
typeof(ProgressViewCard),
1,
propertyChanged: OnThisPropertyChanged);
为方便起见,我认为这两个属性可以共享相同的propertyChanged回调方法,用于计算值。
然后,我们可以实现OnThisPropertyChanged回调方法:
private static void OnThisPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var myCard = bindable as ProgressViewCard;
myCard.OnPropertyChanged(nameof(Score));
myCard.OnPropertyChanged(nameof(Percentage));
}
以下是对我有效的ProgressViewCard.cs的完整代码:
// 代码部分不予翻译
希望这对你有用。
英文:
You have done most of the work. I tried your code and made some changes which worked for me.
First, in Custom control ProgressCardView, delete this first two lines:
private int attempted = 0;
private int possible = 0;
because this has nothing to do with the BindableProperty and you use the following code which will cause the value always be 0. You could set default value in BindableProperty.Create.
public string Score
{
get { return String.Format("{0}/{1}", this.attempted, this.possible); }
}
Second, in BindableProperty, you could define propertyChanged event handler, which could define a callback method when property changed. For more info, you could refer to Detect property changes
And for your case, the change of AttemptedProperty and PossibleProperty would cause the result changed, so we could add propertyChanged to these two BindableProperty. Such like the following:
public static readonly BindableProperty AttemptedProperty = BindableProperty.Create(
nameof(Attempted),
typeof(int),
typeof(ProgressViewCard),
0,
propertyChanged:OnThisPropertyChanged);
public static readonly BindableProperty PossibleProperty = BindableProperty.Create(
nameof(Possible),
typeof(int),
typeof(ProgressViewCard),
1,
propertyChanged:OnThisPropertyChanged);
For convenience, I think these two properties could share the same propertyChanged callback method, that's to count the value.
Then we could implement the OnThisPropertyChanged callback method:
private static void OnThisPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var myCard = bindable as ProgressViewCard;
myCard.OnPropertyChanged(nameof(Score));
myCard.OnPropertyChanged(nameof(Percentage));
}
Here is the complete code for ProgressViewCard.cs which worked for me:
#region BindableProperties
public static readonly BindableProperty TitleProperty = BindableProperty.Create(
nameof(Title),
typeof(string),
typeof(ProgressViewCard),
string.Empty);
public static readonly BindableProperty AttemptedProperty = BindableProperty.Create(
nameof(Attempted),
typeof(int),
typeof(ProgressViewCard),
0,
propertyChanged:OnThisPropertyChanged);
public static readonly BindableProperty PossibleProperty = BindableProperty.Create(
nameof(Possible),
typeof(int),
typeof(ProgressViewCard),
1,
propertyChanged:OnThisPropertyChanged);
private static void OnThisPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var a = bindable as ProgressViewCard;
a.OnPropertyChanged(nameof(Score));
a.OnPropertyChanged(nameof(Percentage));
}
public static readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(
nameof(BackgroundColor),
typeof(Color),
typeof(ProgressViewCard),
Color.FromArgb("#FFFFFF"));
public static readonly BindableProperty HeaderColorProperty = BindableProperty.Create(
nameof(HeaderColor),
typeof(Color),
typeof(ProgressViewCard),
Color.FromArgb("#FFFFFF"));
public static readonly BindableProperty TextColorProperty = BindableProperty.Create(
nameof(TextColor),
typeof(Color),
typeof(ProgressViewCard),
Color.FromArgb("#FFFFFF"));
#endregion
#region Getters and Setters
public string Title
{
get => (string)GetValue(ProgressViewCard.TitleProperty);
set => SetValue(ProgressViewCard.TitleProperty, value);
}
public int Attempted
{
get => (int)GetValue(ProgressViewCard.AttemptedProperty);
set => SetValue(ProgressViewCard.AttemptedProperty, value);
}
public int Possible
{
get => (int)GetValue(ProgressViewCard.PossibleProperty);
set => SetValue(ProgressViewCard.PossibleProperty, value);
}
public string Score
{
get { return String.Format("{0}/{1}", this.Attempted, this.Possible); }
set { this.Score = value; }
}
public string Percentage
{
get { return String.Format("{0:P1}", ((double)this.Attempted) / ((double)this.Possible)); }
set { this.Score = value; }
}
public Color BackgroundColor
{
get => (Color)GetValue(ProgressViewCard.BackgroundColorProperty);
set => SetValue(ProgressViewCard.BackgroundColorProperty, value);
}
public Color HeaderColor
{
get => (Color)GetValue(ProgressViewCard.HeaderColorProperty);
set => SetValue(ProgressViewCard.HeaderColorProperty, value);
}
public Color TextColor
{
get => (Color)GetValue(ProgressViewCard.TextColorProperty);
set => SetValue(ProgressViewCard.TextColorProperty, value);
}
#endregion
#region Methods and Events
public ProgressViewCard()
{
InitializeComponent();
}
private void buttonEvent(object sender, EventArgs e)
{
}
#endregion
Hope it works for you.
答案2
得分: 2
只需在set
方法中添加OnPropertyChanged行,即可使用更少的编码,无需使用propertyChanged方法:
public int Possible
{
get => (int)GetValue(ProgressCardView2.PossibleProperty);
set {
if (SetValue(ProgressCardView2.PossibleProperty, value)) {
OnPropertyChanged(nameof(Score));
OnPropertyChanged(nameof(Percentage));
}
}
}
对于Attempted
重复相同的操作。
这两种方法都可以正常工作。
英文:
An alternative with less coding. Don't need propertyChanged methods.
Just add OnPropertyChanged lines to the set
methods:
public int Possible
{
get => (int)GetValue(ProgressCardView2.PossibleProperty);
set {
if (SetValue(ProgressCardView2.PossibleProperty, value)) {
OnPropertyChanged(nameof(Score));
OnPropertyChanged(nameof(Percentage));
}
}
}
Repeat for Attempted
.
Either approach works fine.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论