Using same ResourceDictionary object in multiple controls - wpf

I have a scenario where I parse the XAML of the resource dictionary at runtime. Like following:
var parsedResourceDictionary = XamlReader.Parse(xaml) as ResourceDictionary;
This all happens inside a custom resource dictionary (derived from ResourceDictionary as base class). After parsing, I call
MergedDictionaries.Add(parsedResourceDictionary);
Since parsing the XAML takes quite some time I want to cache the parsers output and just call the add method on the MergedDictionary field.
Now, my question is if it is possible to keep a reference to this parsedResourceDictionary and add it later.
thanks

Yes. Read it once, put it in a variable somewhere and use that in future instead of reading it again. Did you try it?

I just made a small testing app where I created a ResourceDictionary from an embedded XAML:
public partial class MainWindow : Window
{
public static ResourceDictionary CachedResourceDictionary;
public MainWindow()
{
if (CachedResourceDictionary == null)
{
CachedResourceDictionary = new ResourceDictionary
{
Source =
new Uri("/ResourceDictionaryCache;component/Dictionary1.xaml",
UriKind.RelativeOrAbsolute)
};
}
Resources.MergedDictionaries.Add(CachedResourceDictionary);
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var toOpen = new MainWindow();
toOpen.Show();
}
}
In the Button_Click event I just created a new instance of the MainWindow which then

Related

WPF MVVM share object between view model and code behind

I am trying to share the following object between my view model and code behind
Dictionary<ItemTypeType, Dictionary<string, Dictionary<int,List<ConfigParameter>>>> ItemToConfigParametersValues
This object is not used in the XAML. It is used in the code behind for several dynamically generated UI Elements.
What is the MVVM Light way of doing this?
Well, the ViewModel is typically set as the "DataContext" of the View. Code-behind is part of the View.
So... just expose the data from your ViewModel. In your code-behind, you can access it using the DataContext property (with appropriate casting).
I would store the viewmodel in a variable so I didnt need to cast DataContext everytime... In the MainWindow.xaml.cs code behind for example:
private MainWindowViewModel _vm;
public MainWindow()
{
InitializeComponent();
this._vm = new MainWindowViewModel();
// this._vm.MyProperty = ... (or does the vm instantiate MyProperty?)
this.DataContext = this._vm;
}
private void HandleSomeEvent(object sender, RoutedEventArgs e)
{
var sharedObject = _vm.MyProperty;
}

How to receive the InkCanvas.StrokeCollected event in the view model

In using MVVM pattern, I have a custom inkcanvas with:
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
CustomStroke newStroke = new CustomStroke(e.Stroke.StylusPoints, e.Stroke.DrawingAttributes);
this.Strokes.Remove(e.Stroke);
this.Strokes.Add(newStroke);
InkCanvasStrokeCollectedEventArgs eNew = new InkCanvasStrokeCollectedEventArgs(newStroke);
// Raises the System.Windows.Controls.InkCanvas.StrokeCollected event.
base.OnStrokeCollected(eNew);
}
How do I get the view model to receive the InkCanvas.StrokeCollected event?
I can not bind the XAML to the strokes as the StrokeCollection.CollectionChanged event will be called three times by the custom inkcanvas.
Any help is appreciated.
Try this
public Window3()
{
InitializeComponent();
var vm=new ViewModel();
this.DataContext = vm;
canvas.StrokeCollected += vm.OnStrokeCollected;
}
ViewModel
public class ViewModel
{
public void OnStrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
}
}
Edit
if you want to do it without codebehind see the article EventTrigger
You simply bind it via XAML as you already did, which is the correct way to do it.
That you get 3 events, doesn't matter. Just handle the one you need.
For example, if you are only interested in the StrokeCollectedEvent, then just do
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
if(e.RoutedEvent != InkCanvas.StrokeCollectedEvent)
return;
// handle the event
}
For a full list of Events, consult the "Fields" Section of InkCanvas MSDN documentation. The fields ending with "Event" are RoutedEvent constants, which are passed in the InkCanvasStrokeCollectedEventArgs.

How can i access to a textbox in MainWindow from another form, without creating an instace of it in a WPF?

private void btnConfirmInMyForm_Click(object sender, RoutedEventArgs e)
{
//for example without creating like this
MainWindow mainWin = new MainWindow();
mainWin.txtBirthDate.Text = "anything";
this.close();
}
when i try the above, content of the txtBirthDate of new instance of MainWindow (mianWin) changes to "anything", but not in current MainWindow!
in other words as i click btnConfirmInMyForm in MyForm it opens a new MainWindow with the txtBirthDate textBox contains "anything", which i don't want!
i only want to set the txtBirthDate from MyForm, not to create a new MainWindow that contains this!
with best regards
Is btnConfirmInMyForm_Click within your window? Then just try
this.txtBirthDate.Text = "anything";
If your MainWindow is the Application's MainWindow, then you could try something like:
private void btnConfirmInMyForm_Click(object sender, RoutedEventArgs e) {
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
mainWindow.txtBirthDate.Text = "anything";
}
If it isn't you'd could pass your MainWindow object to the other Window to then use that object and assign the Text. You could also use something like a messaging pattern from MVVM to send messages across Views. There are quite a few options. What you pick is pretty much upto you.

Change WPF mainwindow label from another class and separate thread

Im working on a WPF application. I have a label called "Status_label" in MainWindow.xaml. and I want to change its content from a different class (signIn.cs).
Normally I'm able to do this
var mainWin = Application.Current.Windows.Cast<Window>().FirstOrDefault(window => window is MainWindow) as MainWindow;
mainWin.status_lable.Content = "Irantha signed in";
But my problem is,when I'm trying to access it via different thread in signIn.cs class, it gives an error:
The calling thread cannot access this object because a different thread owns it.
Can I solve this by using Dispatcher.Invoke(new Action(() =>{.......... or something else?
EDIT:
I'm gonna call this label change action from different class as-well-as separate thread
MainWindow.xaml
<Label HorizontalAlignment="Left" Margin="14,312,0,0" Name="status_lable" Width="361"/>
SignIn.cs
internal void getStudentAttendence()
{
Thread captureFingerPrints = new Thread(startCapturing);
captureFingerPrints.Start();
}
void mySeparateThreadMethod()
{
var mainWin = Application.Current.Windows.Cast<Window>().FirstOrDefault(window => window is MainWindow) as MainWindow;
mainWin.status_lable.Dispatcher.Invoke(new Action(()=> mainWin.status_lable.Content ="Irantha signed in"));
}
line var mainWin return errorThe calling thread cannot access this object because a different thread owns it.
Please guide me,
Thank you
I resolved my question, hope somebody will need this. But don't know whether this is the optimized way.
In my mainWindow.xaml.cs :
public MainWindow()
{
main = this;
}
internal static MainWindow main;
internal string Status
{
get { return status_lable.Content.ToString(); }
set { Dispatcher.Invoke(new Action(() => { status_lable.Content = value; })); }
}
from my SignIn.cs class
MainWindow.main.Status = "Irantha has signed in successfully";
This works fine for me.
You can find more details from here, Change WPF window label content from another class and separate thread
cheers!!
try below snippet:
status_lable.Dispatcher.Invoke(...)
Thanks to the answers, they led me in the right direction. I ended up with this simple solution:
public partial class MainWindow : Window
{
public static MainWindow main;
public MainWindow()
{
InitializeComponent();
main = this;
}
}
Then in my eventhandler in another class that runs in a different thred:
internal static void pipeServer_MessageReceived(object sender, MessageReceivedEventArgs e)
{
MainWindow.main.Dispatcher.Invoke(new Action(delegate()
{
MainWindow.main.WindowState = WindowState.Normal;
}));
}
This to show the minimized window when i message is received via a namedPipeline.
Thank you! I wound up with a slightly different solution, but you definitely pointed me in the right direction with your answer.
For my application, I have a lot of controls in main, and most of the method calls on main were occurring from within the scope of main, so it was simpler to use the default { get; set } within MainWindow.xaml.cs (or to just define the controls in XAML).
In my parent window's code-behind, I launch the MainWindow in a separate thread like this (simplified example). The key is to define main globally, even though it is instantiated inside of Window_Loaded():
public ParentWindow()
{
InitializeComponent();
}
MainWindow main;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Thread otherThread = new Thread(() =>
{
main = new MainWindow();
main.Show();
main.Closed += (sender2, e2) =>
main.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
otherThread.SetApartmentState(ApartmentState.STA);
otherThread.Start();
}
Then in my MainWindow code-behind, I just interact with the controls as though it is a simple single-threaded application (there is no control of the parent thread from the child thread in my case). I can, however, control main from the parent thread like this:
private void button_Click(object sender, RoutedEventArgs e)
{
main.Dispatcher.Invoke(new Action(delegate ()
{
main.myControl.myMethod();
}));
}
By doing it this way, I avoid the complexity of defining everything in code-behind and using the dispatcher from within the code-behind of MainWindow.xaml.cs. There are only a few spots in my application where I modify main from the parent window, so this was simpler for me, but your approach seems equally valid. Thanks again!
Simple trick without use Dispatcher.Invoke: In your Window class put this:
public partial class MyWindow: Window
{
public static MyWindow mywin;
[...]
public MyWindow()
{
InitializeComponent();
mywin = this;
[...]
}
[...]
}
Next in your second class to call the properties you need to add your window name + the label that you've assigned. This is an example:
internal class MySecondClass
{
internal void ChangeAValue()
{
MyWindow.mywin.ATextBox.Text = "Some text"; // I'm changing for example the text for a textbox in MyWindow.
}
}

Throw exception when access image from App.xaml.cs

First, try this.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="80" Width="100" Loaded="Window_Loaded">
<Image x:Name="image"/>
</Window>
and
public partial class MainWindow : Window
{
public static BitmapImage okImage = new BitmapImage(new Uri("pack://Application:,,,/ok.png"));
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
image.Source = okImage;
}
}
REMEMBER put any image named ok.png to the project directory. This runs well and shows an image in the window.
Now try to move public static BitmapImage okImage = new BitmapImage(new Uri("pack://Application:,,,/ok.png")); to App.xaml.cs.
Run again, we get TypeInitializationException. Why?
I know I can define okImage in MainWindow.xaml.cs, but if I insist it must be defined and initilized in App.xaml.cs, how should I do?
Usually TypeInitializationException iteslf is not very helpful - always check it's InnerException property.
In your case, it says "Invalid URI: Invalid port specified.", which is much more helpful.
It tells that the problem isn't image itself, it's Uri constructor which throws exception.
But why it succeeds in MainWindow.xaml.cs and fails in App.xaml.cs?
It's all about UriParser schemes. UriParser defines way of parsing some parts of Uri and is involved in Uri object initialization. When your MainWindow object is accessed for the first time, it's static constructor is called and your image is created. At this moment, your application has already registered a UriParser for pack:// scheme, and Uri parser succeeds. But when you try to do the same thing in Application.xaml.cs, custom UriParser for pack:// scheme is not yet registered, and default one fails.
Solution:
Do not instantiate your images in Application static constructor. You may still keep fields or properties and make them static if you want, but move all your image initialization code to OnStartup():
public partial class App : Application
{
public static BitmapImage okImage;
protected override void OnStartup(StartupEventArgs e)
{
okImage = new BitmapImage(new Uri("pack://application:,,,/ok.png"));
base.OnStartup(e);
}
}

Resources