WPF Visualbrush не обновляется, если страница не отображается в рамке - PullRequest
0 голосов
/ 28 июня 2019

Друзья! Есть главное окно, оно содержит рамку, с помощью которой я переключаюсь между страницами. У меня есть страница с холстом. Холст в фоновом потоке получает данные в виде изображений в виде мозаики.

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 при добавлении данных на холст в фоновом потоке, но это не дало результатов.

1 Ответ

1 голос
/ 28 июня 2019

Я предполагаю, что когда вы говорите «перейти на другую страницу», вы имеете в виду что-то вроде:

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

Каждый раз, когда вы делаете это, ваше приложение загружает новый Page из указанного источника.Если текущей страницей является Page с вашим Canvas, навигация создаст новый экземпляр Canvas.Если нет, и JournalEntry.KeepAlive = "true" не установлен для Page с вашим Canvas, то содержимое Frame будет просто воссоздано из файла Source каждыйвремя, когда он отображается, и новый Canvas будет создан с ним.Что-то будет отключено или преждевременно разрушено в какой-то момент.Даже с KeepAlive, установленным на True, вы, вероятно, просто получите несколько экземпляров Canvas, загруженных в память.К какому из них вы хотите привязать ...?

Некоторые альтернативные подходы в моей голове:

  1. Кэшируйте сам Image в вашей View Modelи привяжите к этому как Canvas на Page, так и VisualBrush.

  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
}

Переключение между изображениями в событии Click для Button вне кадра:

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