0

我希望能够在我的 Xamarin.Forms 应用程序中禁用自定义标头 ContentView 中的按钮,以便在离开包含它的 ContentPage 之前警告用户他们有未保存的数据。

我为包含页面绑定其视图模型的 HeaderView 创建了一个 BindableProperty。我受到这篇文章的启发:将具有绑定属性的自定义控件添加到您的 Xamarin.Forms 应用程序 ContentView 的 XAML 在这里

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
             x:Class="HeaderView"
             x:Name="HeaderRoot">


       <Grid RowSpacing="0">
        <Grid.RowDefinitions>
            <RowDefinition Height="80"/>
            <RowDefinition Height="5"/>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" Padding="20,20,20,0" ColumnSpacing="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="1.3*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

        <StackLayout Grid.Column="0">
                <FlexLayout HorizontalOptions="CenterAndExpand" VerticalOptions="Center">
                   <ImageButton x:Name="BackButton" TranslationY="-20" TranslationX="-20" BackgroundColor="Black"  HeightRequest="80" WidthRequest="80"  Source="angleleft.png" Padding="0,0,0,0" Margin="0,0,0,0" Clicked="OnBackClicked" IsEnabled="{Binding Source={x:Reference HeaderRoot}, Path=BindingContext.IsEnabled}" />
                </FlexLayout>
        </StackLayout>

        <FlexLayout Grid.Column="1" JustifyContent="Center" AlignItems="Center">
               <FlexLayout.TranslationY>
                <OnPlatform x:TypeArguments="x:Double">
                    <On Platform="Android" Value="-15"/>
                    <On Platform="iOS" Value="0"/>
                </OnPlatform>
            </FlexLayout.TranslationY>
           <Label x:Name="TitleLabel" FontFamily="{StaticResource TitleFont}" TextColor="White" FontSize="50" />
        </FlexLayout>

           </Grid>
    </Grid>
</ContentView>

ContentView 背后的相关代码在这里

    public partial class HeaderView : ContentView
    {
        public HeaderView()
        {
            InitializeComponent();

            if (DesignMode.IsDesignModeEnabled)
                return; // Avoid rendering exception in the editor.

            //Argument is null because the Header does not need navigation information.
            BindingContext = new HeaderViewModel(null);
        }
        public static BindableProperty CanNavigateProperty = 
                                 BindableProperty.Create(
            propertyName: "CanNavigate",
            returnType: typeof(bool),
            declaringType: typeof(HeaderView),
            defaultValue: true,
            defaultBindingMode: BindingMode.TwoWay,
            propertyChanged: HandleCanNavigatePropertyChanged);

        private static void HandleCanNavigatePropertyChanged(
            BindableObject bindable, 
            object oldValue, 
            object newValue)
        {
            HeaderView targetView = (HeaderView)bindable;

            if (targetView != null)
                targetView.BackButton.IsEnabled = (bool)newValue;
        }

//...

        public bool CanNavigate
        {
            get
            {
                return (bool)base.GetValue(CanNavigateProperty);
            }
            set
            {
                if(this.CanNavigate != value)
                {
                    base.SetValue(CanNavigateProperty, value);
                }
            }
        }


        protected void OnBackClicked(object sender, EventArgs e)
        {
            if (CanNavigate)
            {
                this.Navigation.PopAsync();
            }
        }

    }

编辑. 视图模型非常简单。

    public class HeaderViewModel : ViewModelBase
    {
        public HeaderViewModel(INavigationService navigationService)
            : base(navigationService)
        {
            UserDTO dto = (Prism.PrismApplicationBase.Current as App).CurrentUser;
            UserName = string.Format("{0} {1}", dto.FirstName, dto.LastName);
        }

        private string userName;
        public string UserName 
        { 
            get
            {
                return userName; 
            }
            set
            {
                    SetProperty<string>(ref userName, value);
            }
        }
    }

然后我在包含页面中有以下标记

<mdaViews:HeaderView CanNavigate="{Binding IsSaved}" Title="COLLECTIONS" Grid.Row="0" />

我已经验证了属性 IsSaved 确实改变了它的值。绑定在标签文本中使用时有效。

当我更改 IsSaved 属性的值时,标签将按预期从“true”更改为“false”。然而,这个相同的绑定似乎并没有改变我的自定义标题中的 CanNavigate 值。调试时,OnBackClicked 处理程序始终显示 CanNavigate == true 的值。永远不会输入 propertyChanged 事件 HandleCanNavigatePropertyChanged。如果我应该明确地调用它,我不知道如何。如果我遗漏了什么或使用了错误的方法,我想知道。

编辑 SO中的一些帖子似乎表明将 BindingContext 设置为视图模型可能是问题的一部分。

这是一个这样的例子: ContentView 中的 BindableProperty not working。我不确定应该如何处理视图模型,因为它按预期工作。

4

1 回答 1

0

我在评论中找到了答案,“使用 ContentView 的页面设置了它的 BindingContext,然后它将用于所有子元素,包括自定义 ContentView。如果您再次在内容视图中设置上下文,您将覆盖它。” @Krumelur,他正在回复这个答案https://stackoverflow.com/a/39989721/117995

当我在 ContentView 中设置 BindingContext 时,我覆盖了父页面中所有可用的内容。为了解决这个问题,我将所有 ViewModel 处理移到后面的代码中,并删除了我设置 BindingContext 的代码。

于 2019-08-21T13:16:57.693 回答