【问题标题】:WPF Visualbrush does not update if the page is not displayed in the frame如果页面未显示在框架中,WPF Visualbrush 不会更新
【发布时间】:2019-11-10 03:37:56
【问题描述】:

朋友们! 有一个主窗口,它包含一个框架,我用它在页面之间切换。 我有一个有画布的页面。后台流中的画布以马赛克视图中的图像形式接收数据。

foreach (var item in CoreData.fillWallArray.GetConsumingEnumerable())
{
    if (File.Exists(item.PathFile))
    {
          Application.Current.Dispatcher.Invoke(new Action(() =>
          {
               Image image = new Image();
               image.Source = BitmapImageFromFile(item.PathFile);
               image.Width = (int)Math.Truncate((CoreData.settings.CellWidth * 30) / 2.54);
               image.Height = (int)Math.Truncate((CoreData.settings.CellHeight * 30) / 2.54);
               Canvas.SetLeft(image, item.Column * (int)Math.Truncate((CoreData.settings.CellWidth * 30) / 2.54));
               Canvas.SetTop(image, item.Row * (int)Math.Truncate((CoreData.settings.CellHeight * 30) / 2.54));
               can.Children.Add(image);
          }));
          Thread.Sleep(100);
    }
}

我的任务是将这个画布带到第二个屏幕。为此,我创建了第二个窗口,并作为上下文传递了我需要的画布。

var _BroadcastWindow = new BroadcastWindow();
_BroadcastWindow.DataContext = this.can;
_BroadcastWindow.Show();

在第二个窗口中,我链接数据。

<Grid>
    <Grid.Background>
        <VisualBrush Visual="{Binding}"/>
    </Grid.Background>
</Grid>

一切正常,画布中的数据同步显示在第二个窗口中。但是一旦我切换到另一个页面,Visualbrush 就不再更新了。一旦我切换回带有我在第二个窗口中看到的画布的页面,它就会更新。 可能是什么问题呢? 在后台线程中向画布添加数据时,我也尝试调用 Measure、Arrange、UpdateLayout,但这并没有产生结果。

【问题讨论】:

  • 你的意思是不可见的时候不更新吗?
  • 是的,没错。如果我转到框架中的另一个页面,VisualBrush 不会更新。一旦我返回到画布在框架中的框架,更新就会继续。
  • 如果它是不可见的,为什么需要更新它...?

标签: c# wpf canvas visualbrush


【解决方案1】:

我假设当您说“转到另一个页面”时,您的意思是:

frame.Navigate(new System.Uri("Page2.xaml", UriKind.RelativeOrAbsolute));

每次您执行此操作时,您的应用都会从给定来源加载一个新的Page。如果当前页面恰好是带有您的CanvasPage,导航将创建一个新的Canvas 实例。如果没有,并且您的Canvas 没有为Page 设置JournalEntry.KeepAlive="true",那么Frame 的内容将在每次显示时从Source 文件中重新创建,并且新的@987654331 @ 将用它创建。某些东西会在某个时候断开连接或过早破坏。即使将KeepAlive 设置为True,您也可能最终会在内存中加载多个Canvas 实例。你想绑定哪一个...?

我想到了一些替代方法:

  1. Image 本身缓存在您的视图模型中,并将Canvas 上的PageVisualBrush 绑定到它。

  2. 在您的视图模型中缓存整个Canvas,然后根据需要切换其内容。

第二种方法只需要对您的代码进行少量更改,因此我可以提供一个工作示例(尽管我不知道它是否是最佳的):

Page1.xaml(显示画布的页面)中:

<Grid>
    <ContentControl Content="{Binding canvas, Source={x:Static local:CanvasViewModel.Instance}}" />
</Grid>

BroadcastWindow.xaml:

<Grid>
    <Grid.Background>
        <VisualBrush Visual="{Binding}"/>
    </Grid.Background>
</Grid>

用于保存画布的单例视图模型示例:

public class CanvasViewModel
{   
    Rectangle r = new Rectangle
    {
        Fill = Brushes.Orange,
        Width = 200,
        Height = 100
    };

    Ellipse e = new Ellipse
    {
        Fill = Brushes.DodgerBlue,
        Width = 100,
        Height = 100
    };


    public Canvas canvas { get; set; }

    public void Initialize()
    {
        canvas = new Canvas();
        Switch(1);
    }

    // Here the contents of the canvas are switched
    // I called it from Click events of two Buttons outside of Frame
    // In your case, I imagine it will be something like this:
    // public void LoadImage(string path) {...}
    public void Switch(int imageNr)
    {
        switch (imageNr)
        {
            case 1:
                    canvas.Children.Clear();
                    canvas.Children.Add(r);
                break;
            case 2:
                {
                    canvas.Children.Clear();
                    canvas.Children.Add(e);
                }
                break;
            default:
                break;
        }
    }

    #region CONSTRUCTOR

    static CanvasViewModel() { }
    private CanvasViewModel() { }

    private static CanvasViewModel GetAppViewModelHolder()
    {
        CanvasViewModel vh = new CanvasViewModel();
        vh.Initialize();
        return vh;
    }

    #endregion

    #region SINGLETON Instance

    private static readonly object _syncRoot = new object();
    private static volatile CanvasViewModel instance;

    public static CanvasViewModel Instance
    {
        get
        {
            var result = instance;
            if (result == null)
            {
                lock (_syncRoot)
                {
                    if (instance == null)
                    {
                        result = instance = GetAppViewModelHolder();
                    }
                }
            }
            return result;
        }
    }

    #endregion
}

在框架外ButtonClick 事件中的图像之间切换:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        CanvasViewModel.Instance.Switch(2);
    }

【讨论】:

  • 页面切换如下:GridMain.NavigationService.Navigate(_monitorStatus);其中 _monitorStatus 是页面的一个实例,它被初始化为 MainWindow。这样我就不会每次都加载新页面,在这种情况下也不需要 KeepAlive。
  • 我也尝试使用单例实现。但问题是我需要画布显示在两个地方相同。如果使用单例,那么画布的显示只在一个地方进行。
  • @Maxim_A 上面的 Singleton 实现与您在代码中所做的完全相同:您基本上在两个不同的地方绑定到同一个画布。 “画布的显示只在一个地方进行”是什么意思?
  • 意思是在后台流中,数据到达画布,只在第二个窗口显示。同时,带有画布的页面只是空白。
猜你喜欢
  • 1970-01-01
  • 2016-09-14
  • 1970-01-01
  • 2020-01-29
  • 2012-07-07
  • 2020-10-19
  • 1970-01-01
  • 1970-01-01
  • 2014-08-23
相关资源
最近更新 更多