为什么立即绑定到 Page.Title 不起作用?

huangapple go评论51阅读模式
英文:

Why does binding to Page.Title not work immediately?

问题

主视图中的 TextBlock 应该显示当前页面的标题,但起初是空的。每个页面都有“后退”和“下一步”按钮来切换页面。

在按下“后退”按钮后,正确的标题会立即显示出来。为什么刚开始时标题不可见呢?

答:这是因为在初始化时,TextBlock 的 Text 属性(显示标题的属性)可能尚未得到正确的设置。通常,当您按下“后退”按钮后,StepIndex 属性会更新,导致 CurrentPage 和 StepTitle 属性也更新,进而显示正确的标题。

要确保标题在初始加载时也可见,您可以在 XAML 中为 TextBlock 的 Text 属性设置一个默认值,或者在 ViewModel 的构造函数中初始化 StepTitle 属性,以便在一开始就有一个合适的值。这将使标题在应用程序启动时显示出来,而不仅仅在按下“后退”按钮后才显示。

英文:

I'm in the process of creating an installation wizard for my WPF application. To navigate between the installation steps I'm using a frame control with an array of pages that will be displayed. This is the main view and its view model:

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Border Grid.Row="0"
                Background="{DynamicResource {x:Static ui:Brushes.Layer0BackgroundBrush}}"
                BorderBrush="{DynamicResource {x:Static ui:Brushes.Layer1BorderBrush}}"
                BorderThickness="0,0,0,1">
            
            <StackPanel Orientation="Horizontal">
                <Image Source="{DynamicResource AppIcon}"
                       Margin="10"
                       Height="48"
                       Width="48"/>

                <TextBlock Margin="5,10"
                           FontSize="24"
                           VerticalAlignment="Center"
                           Text="{Binding StepTitle}"/>
            </StackPanel>
        </Border>

        <Frame Grid.Row="1"
               Content="{Binding CurrentPage}"
               NavigationUIVisibility="Hidden"/>
    </Grid>
public class WizardMainViewModel : ObservableObject
{
    static readonly Page main = new WizardMainPage();
    static readonly Page selectEdition = new WizardSelectEditionPage();

    private static readonly Page[] wizardSteps = new Page[]
    {
        main,
        selectEdition
    };

    private int _stepIndex = 0;
    public int StepIndex
    {
        get => _stepIndex;
        set
        {
            SetProperty(ref _stepIndex, value);

            CurrentPage = wizardSteps[value];
            StepTitle = wizardSteps[value].Title;
        }
    }

    private Page _currentPage = wizardSteps[0];
    public Page CurrentPage
    {
        get => _currentPage;
        set => SetProperty(ref _currentPage, value);
    }

    private string _stepTitle = wizardSteps[0].Title;
    public string StepTitle
    {
        get => _stepTitle;
        set => SetProperty(ref _stepTitle, value);
    }
}

The TextBlock in the main view is supposed to display the title of the current page, but at first, it is empty. Every page has 'Back' and 'Next' buttons to switch pages:

private void Cancel(object sender, RoutedEventArgs e)
    {
        Window.GetWindow(this).Close();
    }

    private void Back(object sender, RoutedEventArgs e)
    {
        (Window.GetWindow(this).DataContext as WizardMainViewModel).StepIndex--;
    }

    private void Next(object sender, RoutedEventArgs e)
    {
        (Window.GetWindow(this).DataContext as WizardMainViewModel).StepIndex++;
    }

After you press 'Back', the correct title appears. Why is it not visible right away?

答案1

得分: 2

I tried this code with the MainViewModel instantiated in MainWindow.

That works for me.

My two pages have nothing in them.

My mainwindow has:

<Window.DataContext>
    <local:WizardMainViewModel/>
</Window.DataContext>
<Grid>
    <StackPanel>
        <TextBlock Margin="5,10"
                       FontSize="24"
                       VerticalAlignment="Center"
                       Text="{Binding StepTitle}"/>

    <Frame Grid.Row="1"
           Content="{Binding CurrentPage}"
           NavigationUIVisibility="Hidden"/>
    </StackPanel>
</Grid>
</Window>

为什么立即绑定到 Page.Title 不起作用?

You could try that approach.

If that doesn't work for you.

Instantiate WizardMainViewModel first, then set datacontext of the view to that instance.

I also tried

    private void Window_ContentRendered(object sender, EventArgs e)
    {
        var vm = new WizardMainViewModel();
        this.DataContext = vm;
    }

Which worked.

Your problem here is likely caused by the relative expense of instantiating pages compare to an int. Since I can't reproduce your issue, difficult to prove for certain.

Setting _stepindex to 0 will be very quick.

Building out pages will be far slower.

If the page isn't built when the view hits up that StepTitle getter then there's no title.

You should not be using pages because they have thread affinity and you can't build out this viewmodel on a background thread. You should change to viewmodel first and template out into pages.

Your immediate problem is caused by implicit expectations in the design that make your code fragile.

英文:

I tried this code with the MainViewModel instantiated in MainWindow.

That works for me.

My two pages have nothing in them.

My mainwindow has:

<Window.DataContext>
    <local:WizardMainViewModel/>
</Window.DataContext>
<Grid>
    <StackPanel>
        <TextBlock Margin="5,10"
                       FontSize="24"
                       VerticalAlignment="Center"
                       Text="{Binding StepTitle}"/>

    <Frame Grid.Row="1"
           Content="{Binding CurrentPage}"
           NavigationUIVisibility="Hidden"/>
    </StackPanel>
</Grid>
</Window>

为什么立即绑定到 Page.Title 不起作用?

You could try that approach.

If that doesn't work for you.

Instantiate WizardMainViewModel first, then set datacontext of the view to that instance.

I also tried

    private void Window_ContentRendered(object sender, EventArgs e)
    {
        var vm = new WizardMainViewModel();
        this.DataContext = vm;
    }

Which worked.

Your problem here is likely caused by the relative expense of instantiating pages compare to an int. Since I can't reproduce your issue, difficult to prove for certain.

Setting _stepindex to 0 will be very quick.

Building out pages will be far slower.

If the page isn't built when the view hits up that StepTitle getter then there's no title.

You should not be using pages because they have thread affinity and you can't build out this viewmodel on a background thread. You should change to viewmodel first and template out into pages.

Your immediate problem is caused by implicit expectations in the design that make your code fragile.

huangapple
  • 本文由 发表于 2023年2月26日 21:42:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/75572387.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定