I'm currently working on an editor like WPF application where you can choose to edit a control and then a dialog opens with a copy of the control in it. For the copying of the control I use the XamlReader.Parse(string) and XamlWriter.Save(obj).
Now there are some (custom) controls than contain an Image control who has their Source set to an WriteableBitmap. The Bitmap is not serializable so the serializer is throwing some exceptions, but that's not the main problem because it isn't required to be serialized because it gets changed later anyways.
Is there a way to Serialize a WPF-Control and tell the Serializer to ignore a property of its children or override it with something that is serializeable (like null) in runtime?
I've already tried to manually set the source Property before and after serialization but for some unkown reason I can't change the Source of the deserialized control.
Code for the dialog
public void LaunchEditor()
{
Editor dlg = new Editor ();
dlg.Owner = Application.Current.MainWindow;
dlg.SetTemplateString(XamlWriter.Save(this));
if (dlg.ShowDialog() ?? false)
{
string s = dlg.GetTemplateString();
CustomControlType c = (CustomControlType)XamlReader.Parse(s);
Content = c;
}
}
Code for serialization
public void SetTemplateString(string template)
{
Placeholder = (CustomControlType)XamlReader.Parse(template);
/*Formatting Stuff*/
}
public string GetTemplateString()
{
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings()
{
Indent = true
};
XmlWriter writer = XmlWriter.Create(sb,settings);
XamlDesignerSerializationManager mgr = new XamlDesignerSerializationManager(writer);
XamlWriter.Save(Placeholder, mgr);
return sb.ToString();
}
Custom control that contains the Image
public partial class RollingViewControl : ContentConstrol
{
private RollingBitmap _bmp;
public RollingViewControl()
{
_bmp = new RollingBitmap(256, 256);
InitializeComponent();
ImageDisplay.Source = _bmp.Bitmap;
}
}
RollingBitmap is a Class for Manupilating the WriteableBitmap who is a Property of this Class.
The problem is the Source property of the Image Control, when serializing the Control the Source also gets serialized. Because it is precompiled code I cannot set any Attributes and I haven't found any way to apply XmlAttributeOverrides to the XamlWriter.
Related
I've tried the samples on telerik's website as well, and to no avail. So I have this code:
public ICommand EmailPopUpCmd { get; set; }
private void EmailPopUp(object sender) {
//ToDo: pdf viewer pop up
selectedDataRow = (DataRow)sender;
var window = new Window();
window.Content = new EmailView() { DataContext = this}; //shares the same data context
MemoryStream str = new MemoryStream();//= new MemoryStream(pdfAsByteArray);//new System.Uri(#"pack://application:,,,/Resources/TestPDF.pdf", System.UriKind.RelativeOrAbsolute);
using(FileStream fs = File.OpenRead(#"C:\Source\UI.MailViewer\Resources\TestPDF.pdf")) {
str.SetLength(fs.Length);
fs.Read(str.GetBuffer(),0, (int)fs.Length);
}
AttachmentPath = new PdfDocumentSource(str);
if (window.ShowDialog() == true) {
//from child back to parent
}
}
C:\Source\UI.MailViewer\Resources\TestPDF.pdf
Is where my PDF is located and I bind it to the UI as follow:
telerik:RadPdfViewer x:Name="pdfEmailViewer"
Grid.Row="2"
DocumentSource="{Binding AttachmentPath,Converter={StaticResource DebugConverter}}">
Now using this, I get an object not set to an instance error. Any idea as to why? Using the debug converter, the value of the PDF document created is null, why is that happening?
Okay so to help other users, I found this on the telerik forums: (Telerik Forum Link)
There is one additional thing to do in order to make RadPdfViewer work in a WinForms application: you should ensure that the Application.Current property is initialized before the user control is initialized, for example by adding this code in the constructor of the user control:
public PdfViewerControl() {
if (Application.Current == null)
new Application();
InitializeComponent();
}
This worked for me in WPF as well.
Windows.Media.Captures has a handy CameraCaptureUI class that can be instantiated as follows to show a dialog to the user to capture photos or videos:
// Create dialog to Capture Video
CameraCaptureUI dialog = new CameraCaptureUI();
dialog.VideoSettings.Format = CameraCaptureUIVideoFormat.Mp4;
StorageFile file = await dialog.CaptureFileAsync(CameraCaptureUIMode.Video);
if (file != null)
{
// Do something with file...
}
I would like to create my own custom audio capture class that works in a very similar way:
// Create dialog to Capture Audio
AudioCaptureUI dialog = new AudioCaptureUI();
StorageFile file = await dialog.CaptureFileAsync();
if (file != null)
{
// Do something with file...
}
To do the above, I created the following three files:
AudioCaptureUI - The class that a user instantiates to show the audio capture dialog
AudioCaptureView - UI View for the audio capture experience
AudioCaptureViewModel - ViewModel that contains all the audio capture logic
To create a full screen audio capture dialog, I have figured out that the best way is to use a Popup and set its child to the AudioCaptureView. The problem I have with this approach is that it is pushing me use a View-First pattern. Since I am using Caliburn Micro, I wanted to be able to use CM to instantiate a View by creating the ViewModel first.
What I currently have is something on the following lines:
public class AudioCaptureUI
{
private Popup _popup;
private TaskCompletionSource<StorageFile> _taskCompletionSource;
public IAsyncOperation<StorageFile> CaptureFileAsync()
{
// Force my View to be full screen
AudioCaptureView audioCaptureView = new AudioCaptureView
{
Width = Window.Current.Bounds.Width,
Height = Window.Current.Bounds.Height
};
// Creating View, instead of a ViewModel. Renders Caliburn Micro useless!
_popup = new Popup { Child = audioCaptureView };
if (_popup.Child != null)
{
SubscribeEvents();
_popup.IsOpen = true;
}
return AsyncInfo.Run(WaitForInput);
}
...
}
The above pattern works. However, I am forced to wire all my actions manually and cannot leverage Caliburn Micro's MVVM goodness.
How else should I instantiate a ViewModel programatically from my AudioCaptureUI class?
It is also important to highlight that I am working on a Windows Store app and using the WinRT CM port.
You could always port the WindowManager to WinRT in your own project. Looking at the source I don't think too much will need to change. https://caliburnmicro.codeplex.com/SourceControl/latest#src/Caliburn.Micro.Platform/net40/WindowManager.cs
You could bring over the Interface as well and use DI but for the sake of time here is the stand alone class. The main part for Model first binding is the ViewLocator.LocateForModel which returns the View from the ViewModel (aka the magic)
using System;
using System.Collections.Generic;
using Windows.UI.Xaml.Controls.Primitives;
namespace Caliburn.Micro
{
public class WindowManager
{
public virtual void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
var popup = CreatePopup(rootModel, settings);
var view = ViewLocator.LocateForModel(rootModel, popup, context);
popup.Child = view;
//popup.SetValue(View.IsGeneratedProperty, true);
ViewModelBinder.Bind(rootModel, popup, null);
Caliburn.Micro.Action.SetTargetWithoutContext(view, rootModel);
var activatable = rootModel as IActivate;
if (activatable != null)
{
activatable.Activate();
}
var deactivator = rootModel as IDeactivate;
if (deactivator != null)
{
popup.Closed += delegate { deactivator.Deactivate(true); };
}
popup.IsOpen = true;
//popup.CaptureMouse();
}
protected virtual Popup CreatePopup(object rootModel, IDictionary<string, object> settings)
{
var popup = new Popup();
ApplySettings(popup, settings);
return popup;
}
bool ApplySettings(object target, IEnumerable<KeyValuePair<string, object>> settings)
{
if (settings != null)
{
var type = target.GetType();
foreach (var pair in settings)
{
var propertyInfo = type.GetPropertyCaseInsensitive(pair.Key);
if (propertyInfo != null)
{
propertyInfo.SetValue(target, pair.Value, null);
}
}
return true;
}
return false;
}
}
}
Then all you need to do is create an instance and give it a ViewModel:
var windowManager = new WindowManager();
windowManager.ShowPopup(new MyPopupThingViewModel());
Note: I've only used this in an 8.1 app so not 100% sure if it will completely work with 8.0
You might have some success with the Caliburn.Micro WindowManager. There isn't a great deal about it in the official documentation (you're best off searching Google and the CM discussions). I've used it in one of my applications where I needed to host a particular ViewModel in a new window, and wanted to utilise all of the Caliburn.Micro goodness (and my existing Views).
Have a look at the Caliburn.Micro.IWindowManager interface, you'll see some handy methods that you can call from a WindowManager instance (depending on the popup type you're after).
public interface IWindowManager
{
bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null);
void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null);
void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null);
}
In my application, to pop up my a Window with my ViewModel of choice, I did something along these lines (your names inserted):
// Some basic Window settings.
dynamic settings = new ExpandoObject();
settings.Title = "Test Window";
settings.WindowStartupLocation = WindowStartupLocation.Manual;
settings.SizeToContent = SizeToContent.Manual;
settings.Width = 450;
settings.Height = 300;
var localAudioCaptureViewModel new AudioCaptureViewModel ();
WindowManagerFactory.WindowManager.ShowWindow(localAudioCaptureViewModel, null, settings); // I didn't require context (null)
Caliburn.Micro should resolve your Views to the correct ViewModels, and you're good to go.
Hi I have read through some of the relevant questions in stackoverflow but still can't figure out how to solve my questions:
I need to creat a CheckableGroupBox control which is same as GroupBox but has a checkbox in the header. And it has the following requirement:
The original GroupBox has a Header property that is of Object type, I need to limit this property in the CheckableGroupBox to be string only.
Users can change the header's text (the checkbox's text) by calling myCheckableGroupBox.Header="some text here".
I wrote the below code to serve these proposes:
public class CheckableGroupBox : System.Windows.Controls.GroupBox
{
//override the default header property
private static void OnHeaderChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
CheckableGroupBox me = o as CheckableGroupBox;
me.labHeader.Content = e.NewValue.ToString();
}
public CheckableGroupBox() : base()
{
//setup the header of the group box
//create the stack panel, add the checkbox and the label
System.Windows.Controls.StackPanel sp = new System.Windows.Controls.StackPanel();
sp.Orientation = System.Windows.Controls.Orientation.Horizontal;
this.chkHeader = new System.Windows.Controls.CheckBox();
sp.Children.Add(this.chkHeader);
sp.Children.Add(this.labHeader);
//set the header to be the stack panel.
this.Header = sp;
//override the default HeaderProperty
CheckableGroupBox.HeaderProperty.OverrideMetadata(
typeof(object),
new PropertyMetadata("", OnHeaderChangedCallback)
);
}
}
However, the code doesn't work. When I add a CheckableGroupBox(in the design view) to a window, it prompts:
Cannot create an instance of "CheckableGroupBox", Object' type must derive from DependencyObject.
Any ideas?
You should use:
System.Windows.Controls.GroupBox.HeaderProperty.OverrideMetadata(
typeof(CheckableGroupBox),
new PropertyMetadata("", OnHeaderChangedCallback)
);
[CommonDependencyProperty]
public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(typeof (Control), (PropertyMetadata) new FrameworkPropertyMetadata((object) SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits));
I have an custom control that represents a grid; and implements another custom control.
When opening this control in de designer, I am able to use the collection editor to set my collection. When saving; the designer successfully saves my collection.
However, when dropping this control on a form; it still (and should) expose(s) the collection property allowing me to modify the default values as i have defined in the other control.
However; when saving this designer; it also tries to store the predefined items in the collection; adding the default ones with every save.
What is the best way to solve this problem? I have attached a code sample.
Code sample where i have defined my collection:
GridPicture.cs
[Category("Layout")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor(typeof(CollectionEditor), typeof(UITypeEditor))]
public GridPictureColumnDefinitionCollection ColumnDefinitions
{
// The DesignerSerializationVisibility attribute instructs the design editor to serialize the contents of the collection to source code.
// This will place all the code required to add the items to a collection variable of GridPictureColumnDefinitionCollection.
get
{
return m_ColumnDefinitions;
}
}
Generated designer code of my first 'implementation' of this grid; Picture1.cs
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition1 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition2 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureRowDefinition gridPictureRowDefinition1 = new VGTest.GridPictureRowDefinition();
gridPictureColumnDefinition1.Auto = true;
gridPictureColumnDefinition1.Value = 0F;
gridPictureColumnDefinition2.Auto = true;
gridPictureColumnDefinition2.Value = 0F;
this.ColumnDefinitions.Add(gridPictureColumnDefinition1);
this.ColumnDefinitions.Add(gridPictureColumnDefinition2);
gridPictureRowDefinition1.Auto = true;
gridPictureRowDefinition1.Value = 0F;
this.RowDefinitions.Add(gridPictureRowDefinition1);
Code sample when i place this picture1 on another picture; picture2.cs: (Note that picture11 is the picture1, as it is the 1st of picture1 ;)
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition1 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition2 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureColumnDefinition gridPictureColumnDefinition3 = new VGTest.GridPictureColumnDefinition();
VGTest.GridPictureRowDefinition gridPictureRowDefinition1 = new VGTest.GridPictureRowDefinition();
// Some code removed that does the Auto and Value settings as above
this.picture11.ColumnDefinitions.Add(gridPictureColumnDefinition1);
this.picture11.ColumnDefinitions.Add(gridPictureColumnDefinition2);
this.picture11.ColumnDefinitions.Add(gridPictureColumnDefinition3);
The picture2 control; when it regenerates the InitializeComponent() method; now adds the columndefinitions which i have added in picture1.
I have written this temporary fix for the problem:
{
// This makes sure column definitions are only serialized when configured at a implementation of this GridPicture.
// This is a Quick/Dirty fix for the following problem:
// When MyPanel (:GridPicture) is put on PanelContainer(:Picture); the picture designer (re)serializes this each save.
return this.GetType().BaseType.Name == typeof(Picture).Name;
}
I was unable to find a better method. I have decided to stick with the solution below.
{
// This makes sure column definitions are only serialized when configured at a implementation of this GridPicture.
// This is a Quick/Dirty fix for the following problem:
// When MyPanel (:GridPicture) is put on PanelContainer(:Picture); the picture designer (re)serializes this each save.
return this.GetType().BaseType.Name == typeof(Picture).Name;
}
I am trying to create some custom treeviews. Everything is working fine so far, but I got a little problem with styles. I have a simple "RedBackground" Style which I add to the resources of the Window. When adding normal elements, it works fine.
When using a custom item template to render treeview items, my resource is ignored. If I add the resource directly to the template it works fine (as marked in code)...
I obviously do not want to have to add styles to the ItemTemplate direclty, would be very complicated in further development. I think I am missing some kind of "Binding" or "Lookup"... I think it is related to dependency properties... Or something in this direction.
Perhaps anyone has more insights, here is the code creating the template (inside util class, but thats just to keep all clean):
var hdt = new HierarchicalDataTemplate(t)
{
ItemsSource = new Binding("Children")
};
var tb = new FrameworkElementFactory(typeof (TextBlock));
tb.SetBinding(TextBlock.TextProperty, new Binding("Header"));
hdt.VisualTree = tb;
// This way it works...
TextBlockStyles.AddRedBackground(hdt.Resources);
return hdt;
And here my very simple custom tree view
public class TreeViewCustom<T> : TreeView
{
public TreeViewCustom()
{
MinWidth = 300;
MinHeight = 600;
ItemTemplate = TreeViewTemplates.TryGetTemplate(typeof(T));
// This is ignored.... (Also when set as resource to window)
TextBlockStyles.AddRedBackground(Resources);
}
}
Ok, and to be sure, here the code which creates the Style:
public static class TextBlockStyles
{
public static void AddRedBackground(ResourceDictionary r)
{
var s = CreateRedBackground();
r.Add(s.TargetType, s);
}
private static Style CreateRedBackground()
{
var s = new Style(typeof(TextBlock));
s.Setters.Add(new Setter
{
Property = TextBlock.BackgroundProperty,
Value = new SolidColorBrush(Colors.Red)
});
return s;
}
}
Thanks for any tips...
Chris
Is this a problem with "inheritance"? Not all properties are inherited, read more here:
Property Value Inheritance: http://msdn.microsoft.com/en-us/library/ms753197.aspx