背景
我正在尝试为我的Xamarin Forms Shell应用程序制作一个可自定义,可重用的标题视图模板。所谓“标题视图”,我指的是出现在页面顶部的标题栏。**我已经基本实现了它,但我正在努力使一些绑定工作。
这个想法很简单。我想最终得到这样的东西,一个非常标准的标题栏模板,我可以在逐页的基础上填充空白:
我希望能够在我的任何ContentPages
中创建它。我已经做了调整,这样我只需要告诉按钮它们的图像源和命令是什么,其余的将为我格式化:
<ContentPage
x:DataType="viewmodels:MyViewModel"
... >
<Shell.TitleView>
<tv:CustomAppShellTitleView>
<tv:CustomAppShellTitleView.LeftButton>
<tv:TitleViewButtonInfo Command="{Binding SomeCommandHere}" Source="{StaticResource SomeIconHere}" />
</tv:CustomAppShellTitleView.LeftButton>
<tv:CustomAppShellTitleView.Title>SomeTitle</tv:CustomAppShellTitleView.Title>
<tv:CustomAppShellTitleView.RightButton1>
<tv:TitleViewButtonInfo Command="{Binding SomeCommandHere}" Source="{StaticResource SomeIconHere}" />
</tv:CustomAppShellTitleView.RightButton1>
<tv:CustomAppShellTitleView.RightButton2>
<tv:TitleViewButtonInfo Command="{Binding SomeCommandHere}" Source="{StaticResource SomeIconHere}" />
</tv:CustomAppShellTitleView.RightButton2>
<tv:CustomAppShellTitleView.RightButton3>
<tv:TitleViewButtonInfo Command="{Binding SomeCommandHere}" Source="{StaticResource SomeIconHere}" />
</tv:CustomAppShellTitleView.RightButton3>
</tv:CustomAppShellTitleView>
</Shell.TitleView>
...
我的所有ContentPages
都使用视图模型,我想使用它们的数据来配置标题栏行为(例如,按钮命令,可见性等)。
TitleViewButtonInfo
你会注意到我使用了一个叫做TitleViewButtonInfo
的东西。我想既然我有4个可能的按钮,我可以通过将它们的Bindable Properties放在一个漂亮的小对象中来减少代码重复,就像这样:
using System.Windows.Input;
using Xamarin.Forms;
namespace MyNamespace.Views.TitleViews
{
public class TitleViewButtonInfo : BindableObject
{
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(TitleViewButtonInfo), default(ICommand));
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(ImageSource), typeof(TitleViewButtonInfo), default(ImageSource));
public ImageSource Source
{
get => (ImageSource)GetValue(SourceProperty);
set
{
SetValue(SourceProperty, value);
OnPropertyChanged(nameof(IsVisible));
}
}
public bool IsVisible => Source != null;
}
}
CustomAppShellTitleView
最后,我得到了我的 actualCustomAppShellTitleView
。为了简单起见,我将省略右侧的按钮:
XAML
<ContentView
x:Class="MyNamespace.Views.TitleViews.CustomAppShellTitleView"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyNamespace.Views.TitleViews"
x:Name="customAppShellTitleView"
x:DataType="local:CustomAppShellTitleView">
<ContentView.Resources>
<ResourceDictionary>
<x:Double x:Key="ButtonSize">32</x:Double>
</ResourceDictionary>
</ContentView.Resources>
<ContentView.Content>
<Grid
Padding="0,0,10,0"
RowSpacing="0"
VerticalOptions="CenterAndExpand">
<Grid.ColumnDefinitions>
<!-- Left-side Button -->
<ColumnDefinition Width="Auto" />
<!-- Title -->
<ColumnDefinition Width="*" />
<!-- Right-side Buttons -->
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Left-side Button -->
<ImageButton
Grid.Column="0"
Command="{Binding LeftButton.Command, Source={x:Reference Name=customAppShellTitleView}}"
HeightRequest="{StaticResource ButtonSize}"
HorizontalOptions="Start"
IsVisible="{Binding LeftButton.IsVisible, Source={x:Reference Name=customAppShellTitleView}}"
Source="{Binding LeftButton.Source, Source={x:Reference Name=customAppShellTitleView}}"
WidthRequest="{StaticResource ButtonSize}" />
<!-- Title -->
<Label
Grid.Column="1"
HorizontalOptions="StartAndExpand"
LineBreakMode="TailTruncation"
MaxLines="1"
Text="{Binding Title, Source={x:Reference Name=customAppShellTitleView}}"
VerticalOptions="CenterAndExpand" />
<!-- Right-side Buttons -->
<StackLayout
Grid.Column="2"
Orientation="Horizontal"
Spacing="10">
<!-- Buttons 1, 2 and 3 here -->
</StackLayout>
</Grid>
</ContentView.Content>
</ContentView>
代码隐藏
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace MyNamespace.Views.TitleViews
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomAppShellTitleView : ContentView
{
// LeftButton
public static readonly BindableProperty LeftButtonProperty = BindableProperty.Create(
nameof(LeftButton),
typeof(TitleViewButtonInfo),
typeof(CustomAppShellTitleView),
new TitleViewButtonInfo());
public TitleViewButtonInfo LeftButton
{
get => (TitleViewButtonInfo)GetValue(LeftButtonProperty);
set => SetValue(LeftButtonProperty, value);
}
// Same thing for RightButton1, 2 and 3
#region Title Property
public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string), typeof(CustomAppShellTitleView), default(string));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
#endregion
public CustomAppShellTitleView()
{
InitializeComponent();
BindingContext = this;
}
}
}
问题
这种设置似乎在大部分情况下都有效。一切看起来都很好。标题和按钮图像源在运行时被正确设置,主要是因为它们是静态值,在我的页面的XAML中设置Shell.TitleView
时就存在了。但是,绑定是问题所在。例如,按钮在被按下时什么也不做,尽管在我的视图模型中它们的Command
属性绑定到一些ICommand
。当我将此命令绑定到视图中的常规按钮时,它可以正常工作,因此这不是由于XAML和视图模型之间的某些不匹配。
很明显,这里有一些基本的东西我不明白。
What I've Tried
我在TitleViewButtonInfo.CommandProperty
和视图模型的构造函数中设置了断点,命令被分配到这里。由于视图是在视图模型被设置为BindingContext(甚至是现有的)之前初始化的,所以这是有意义的--但是一旦视图模型实际设置了CommandProperty
的setter,它就再也不会被命中了。显然,第一次命中时,值将为null,因为视图模型尚未初始化。因此,即使在我的视图模型中设置了ICommand *,title视图也听不到它。我尝试在已绑定的命令上触发OnPropertyChanged
,但不起作用。
有没有什么方法可以让它在我的视图模型中设置命令时听到?必须有一些明显的,我做错了这样的标题视图的属性只能设置 * 一次 *,永远不会再次。
1条答案
按热度按时间cnjp1d6j1#
TitleViewButtonInfo的BindingContext不是页的ViewModel。所以你可以为它设置绑定源。下面是一个例子:
希望能成功。