Reputation: 377
Friends! There is a main window, it contains a frame with which I switch between pages. I have a page that has a canvas. Canvas in the background stream receives data in the form of images in a mosaic view.
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);
}
}
My task is to bring this canvas to the second screen. To do this, I create a second window and, as a context, pass the canvas that I need.
var _BroadcastWindow = new BroadcastWindow();
_BroadcastWindow.DataContext = this.can;
_BroadcastWindow.Show();
And in the second window, I link the data.
<Grid>
<Grid.Background>
<VisualBrush Visual="{Binding}"/>
</Grid.Background>
</Grid>
Everything works fine, data from the canvas synchronously displayed in the second window. But as soon as I switch to another page, the Visualbrush is no longer updated. As soon as I switch back to the page with the canvas I see in the second window, it is updated. What could be the problem? I also tried to call Measure, Arrange, UpdateLayout when adding data to the canvas in the background thread, but this did not produce results.
Upvotes: 0
Views: 394
Reputation: 5373
I assume when you say "go to another page" you mean something along the lines of:
frame.Navigate(new System.Uri("Page2.xaml", UriKind.RelativeOrAbsolute));
Every time you do this, your app loads a new Page
from a given source. If the current page happens to be the Page
that has your Canvas
on it, navigation will create a new Canvas
instance. If not, and there is no JournalEntry.KeepAlive="true" set for the Page
with your Canvas
, then contents of the Frame
will just get recreated from the Source
file every time it is displayed, and a new Canvas
will be created with it. Something will get disconnected or prematurely destroyed at some point. Even with KeepAlive
set to True
, you'll probably just end up with multiple instances of Canvas
loaded in memory. Which one do you want to bind to...?
Some alternative approaches off the top of my head:
Cache the Image
itself in your View Model and bind both your Canvas
on the Page
and the VisualBrush
to that.
Cache the whole Canvas
in your View Model, then switch its contents as needed.
The second approach required only minimal changes to your code, so I could throw in a working example (although I don't know if it's the most optimal):
In Page1.xaml
(the page that displays the Canvas):
<Grid>
<ContentControl Content="{Binding canvas, Source={x:Static local:CanvasViewModel.Instance}}" />
</Grid>
In BroadcastWindow.xaml
:
<Grid>
<Grid.Background>
<VisualBrush Visual="{Binding}"/>
</Grid.Background>
</Grid>
Example singleton View Model to hold the canvas:
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
}
Switching between images in the Click
event of a Button
outside of Frame:
private void Button_Click(object sender, RoutedEventArgs e)
{
CanvasViewModel.Instance.Switch(2);
}
Upvotes: 1