I am currently working on a SilverLight 3 Application. I am using MVVM Pattern and Prism. I have everything working except the following item. On one of my views I have to use an OpenFileDialog. I attempted to do this in the ViewModel only to find out the security model of SilverLight prohibits it because it is only allowed to be user initiated. I've since moved the OpenFileDialog code to the code-behind of the View. Here is my problem though. Although I have binding to the source set to TwoWay it is not hitting the setter of the property in my ViewModel.
Example of Image control with binding:
<Image x:Name="imgCard" Height="283" Width="463" Canvas.Left="8" Canvas.Top="8" OpacityMask="White" Source="{Binding Path=CardImage, Mode=TwoWay}"/>
Button Used by user:
<Button x:Name="btnUpload" Height="20" Width="122" Canvas.Left="8" Canvas.Top="319" Content="Upload Image" Click="btnUpload_Click" />
Click Event:
private void btnUpload_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "PNG Files(*.png)|*.png";
ofd.ShowDialog();
using (Stream stream = ofd.File.OpenRead())
{
BitmapImage image = new BitmapImage();
image.SetSource(stream);
imgCard.Source = image;
}
}
My ViewModel is implementing the INotifyPropertyChanged and has the following property.
BitmapSource CardImage
{
get
{
return _imageSource;
}
set
{
_imageSource = value;
NotifyPropertyChanged("CardImage");
}
}
If I put a break point on the Setter. It never hits it.
At least in Silverlight 2, I think the following rule might explain why you are seeing this behavior. "If a Dependency Property is bound and in code the property is set to a value explicitly, the binding is removed." (source)
Maybe this has changed for Silverlight 3? In that case, I have no suggestions.
Ok this is a hack but it works. Because I have to fire the OpenFileDialog from the UI I can instead of updating the control directly reverse tether to the DataContext to update the property. This works and still renders the UI the way I expect.
NOTE: HACK Until I find a better way.
private void btnUpload_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "PNG Files(*.png)|*.png";
ofd.ShowDialog();
using (Stream stream = ofd.File.OpenRead())
{
BitmapImage image = new BitmapImage();
image.SetSource(stream);
BitmapSource b = image;
//HACK: This works but now I'm tethered a bit. This updates the context property CardImage.
((DesignerViewModel) this.DataContext).CardImage = b;
//imgCard.Source = b;
}
}
Related
So I'm trying to use David Veeneman's Bindable WPF RichTextBox here in my .net 4.5 project. After adding the control and the ValueConverter in my code I noticed only the the public object Convert() will be triggered but the public object ConvertBack() not.
After reading the comments to this project I changed following parts of the control source code.
private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = (EcoRichTextBox)d;
if (thisControl.m_InternalUpdatePending > 0)
{
thisControl.m_InternalUpdatePending--;
return;
}
// Changed:
try
{
thisControl.TextBox.Document = (e.NewValue == null) ? new FlowDocument() : (FlowDocument)e.NewValue;
}
catch { }
thisControl.m_TextHasChanged = false;
}
And this Event Handler:
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
// Set the TextChanged flag
m_TextHasChanged = true;
// Changed:
Document = TextBox.Document;
}
Now the the both method of the ValueConverter worked fine but events like private void OnNormalTextClick(object sender, RoutedEventArgs e) causes a FatalExecutionEngineError on Runtime.
So i wonder if there are major changes form WPF 3.5 to 4.5?
Or anybody have an idea to work around this?
Update
Binding in XAML
<uc:FsRichTextBox Margin="5"
Document="{Binding Path=Ereignis.Bericht,
Converter={StaticResource flowDocumentConverter},
UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" />
I ran the demo you linked here in VS2015 with target framework 4.0 and 4.5. It will not update when I take out the two way data binding.
Add to your RTB. Two way data binding and a name:
Mode=TwoWay
x:Name="EditBox"
I think rather than managing the text change yourself here, remove this:
// Changed:
Document = TextBox.Document;
Use an event handler to update the data.
Then in your event handler that is managing your updates (I am assuming a button click? And allow this to manage the update.
this.EditBox.UpdateDocumentBindings();
The x:name attribute is valuable.
This is all found in the source code.
If you can be more clear about how your project is arranged I can provide more detail. But for starters, I would do this. Stick more closely to the provided example.
I've created a custom usercontrol that's composed of a AutoCompleteBox with a Selected Item... till now I've implemented it in a way I don't like... I mean I've a XAML view, a Viewmodel and in the viewmodel I load data from a stored procedure.
Since the AutoComplete box is a third party UserControl I've added it to the XAML view and not defined as a custom usercontrol. What's the best practice to do so?
I think the fact that I'm using Catel as MVVM Framework is irrilevant right now..
Thanks
UPDATE #1
My usercontrols need to have some properties that are passed via XAML for example (LoadDefaultValue)
<views:PortfolioChooserView x:Name="PortfolioChooserView" DataContext="{Binding Model.PortfolioModel}" Height="25" LoadDefaultValue="True" Width="150" />
To achieve such a scenario I had to define a dependency property in my PortfolioChooserView defined as
public bool LoadDefaultValue
{
get { return (bool)GetValue(LoadDefaultValueProperty); }
set { SetValue(LoadDefaultValueProperty, value); }
}
public static readonly DependencyProperty LoadDefaultValueProperty = DependencyProperty.Register(
"LoadDefaultValue", typeof(bool), typeof(PortfolioChooserView), new PropertyMetadata(default(bool)));
Since if I would have defined it in Viewmodel only I wouldn't have been able to set it.
The odd thing is that in order to pass it to the viewmodel I had to do such a trick
public PortfolioChooserView()
{
InitializeComponent();
if (!isFirstLoad) return;
Focusable = true;
PortfolioCompleteBox.AllowDrop = true;
PortfolioCompleteBox.Focus();
DragDropManager.AddPreviewDragOverHandler(PortfolioCompleteBox, OnElementDragOver);
DragDropManager.AddDropHandler(PortfolioCompleteBox, OnElementDrop);
DataContextChanged += PortfolioChooserView_DataContextChanged;
isFirstLoad = false;
}
void PortfolioChooserView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var dataContext = DataContext as PortfolioModel;
if (dataContext != null)
{
dataContext.LoadDefaultValue = LoadDefaultValue;
dataContext.AllowNull = AllowNull;
//var converter = new PortfolioConverter();
//var portfolio = (Portfolio) converter.Convert(SelectedItem, null, null, CultureInfo.CurrentCulture);
//dataContext.SelectedItem = portfolio;
}
}
But I really dislike to use the DataContextChanged event ...do you see a better approach?
Thank
UPDATE#2
I keep this toghether since It's a related question...
On some viewmodel I used DeferValidationUntilFirstSaveCall = true; in the Constructor to disable the validation at load but my custom usercontrols shows the red border around... what should I do to propagate that info to the nested usercontrols?
Thanks again
See Orc.Controls for tons of examples. It's an open-source library that has a lot of user controls built with Catel, even one with an auto complete box.
I'm currently learning how to write a WPF application using the MVVM pattern. I'm writing a little contact manager application, so my app displays a Listbox bound to my View Model, and a set of fields bound to ListBox.SelectedItem. One of these fields is the contact's photo.
I'd like to change the photo in the edit part using OpenFileDialog, so the Listbox item would be updated, as it is for all of the other fields.
I first tried to update the source property of the Image control, but doing this, I lose the Binding...
Then I wrote an handler on Button_Click to update the Contact.Photo property (its type is byte[]), and it works. But instead of binding from the "update control" to the view model, binding is from the VM to the control, as if the data came from the DB.
(In the code, LoadPhoto returns a byte[])
private void Button_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog OpenFileDialog = new OpenFileDialog();
if (OpenFileDialog.ShowDialog() == true)
{
(listbox.SelectedItem as ContactManager.ViewModel.Contact).Photo =
LoadPhoto(OpenFileDialog.FileName);
}
}
I wonder if it doesn't break the MVVM pattern... I'm not sure of what could be made in the View... Is it the right way to update the Contact object ? Does anyone have a better solution to this problem ?
Look into binding your button to a Command Binding instead of the click event.
You can find implementations of DelegateCommand using Google.
Next you can expose a ImageSource from your ViewModel that you can bind to your Image from your XAML.
I've included some code fragments to get you started.
Once you get past the basics take a look at MVVM Frameworks, like Cinch, you'll find a way to handle OpenFileDialog using the Services Interfaces IOpenFileService.cs to not violate the MVVM pattern.
Here is the XAML:
<Button Content="Update Photo" Command="{Binding UpdatePictureCommand}"/>
<Image Source="{Binding EmployeePicture}"
VerticalAlignment="Center" HorizontalAlignment="Center"
Stretch="Fill" />
Here is the ViewModel:
public MainViewModel()
{
UpdatePictureCommand = new DelegateCommand<object>(OnUpdatePictureCommand, CanUpdatePictureCommand);
}
public ICommand UpdatePictureCommand { get; private set; }
private void OnUpdatePictureCommand(object obj)
{
OpenFileDialog OpenFileDialog = new OpenFileDialog();
if (OpenFileDialog.ShowDialog() == true)
{
//(listbox.SelectedItem as ContactManager.ViewModel.Contact).Photo =
// LoadPhoto(OpenFileDialog.FileName);
Stream reader = File.OpenRead(OpenFileDialog.FileName);
System.Drawing.Image photo = System.Drawing.Image.FromStream((Stream)reader);
MemoryStream finalStream = new MemoryStream();
photo.Save(finalStream, ImageFormat.Png);
// translate to image source
PngBitmapDecoder decoder = new PngBitmapDecoder(finalStream, BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
EmployeePicture = decoder.Frames[0];;
}
private bool CanMoveFirstCommand(object obj)
{
return true;
}
private ImageSource _employeePicture;
public ImageSource EmployeePicture
{
get
{
return _employeePicture;
}
set
{
_employeePicture = value;
OnPropertyChanged("EmployeePicture");
}
}
What would be the best way to save the window position and size in a WPF app?
Currently, I'm saving the window size and position of a WPF App. Here are the events I handle:
SourceInitialized : The saved info is loaded on to the window
WindowClosing : The current info is saved to the backing store
(I copied this from an example).
The problem is, when the window is minimized and restored, the settings from the last WindowClosing is retrieved.
Now, the StateChanged event fire AFTER the window has minimized, so it does not seem to be what i need.
Thanks
Do yourself and your users a favor and use the LocationChanged event and the SizeChanged event to save the settings at that time. There's nothing more annoying than an application that gets amnesia if the process exits abnormally and settings don't get saved (cough...explorer...cough...)
Then just check to make sure the WindowState == Normal before saving the settings. Obviously its pointless to save the position of a minimized or maximized window.
As for when to load the settings, well that you can just do in the constructor after the InitializeComponent call or you can use the Initialized event. No real reason to use the SourceInitialized event unless you are doing something with the HWND directly which shouldn't be necessary.
Use the WindowInteropHelper object to get the window handle and use Screen.FromHandle method to get the actual screen the window is on. When saving make sure to also save the screen bounds just in case it does not exist any more.
One caveat when restoring the screen to its former state is it has to be done after the window handle is created so can't do it in the constructor else won't work properly in multiple monitor situations. Try doing it on the SourceInitialized callback
Are you doing this via databinding? That is the way I do my window sizing and position. I typically have a UserConfig.xml file that is saved in the Users Profile. Then I create elements in there as I databind them in the program. I have the Application.xaml resource dictionary refer to that file, and all of the settings I want set to XPaths inf the XML. Then I just save the in memory xml document on exit. Only one event to handle, no mess, no fuss.
And you can expand it to encompass as many settings as you like in regard to the UI. Adding plumbing settings is a little more difficult, but not terribly so.
I have a solution for saving Size and State, you can extend it to also save the Position. It's done using a Behavior. Simply Binding the Width and Height did not work as expected, because it would overwrite the "Normal" state's size with the maximized sizes. That's why there are some extra checks like if(state == normal)
There is a Config Property on my Window's DataContext.
You'll need a reference to System.Windows.Interactivity to do that.
public class MainWindowSaveStateBehavior : Behavior<Window>
{
public Config Config
{
get { return (Config)GetValue(ConfigProperty); }
set { SetValue(ConfigProperty, value); }
}
public static readonly DependencyProperty ConfigProperty =
DependencyProperty.Register("Config", typeof(Config), typeof(MainWindowSaveStateBehavior), new PropertyMetadata(Config_Changed));
private static void Config_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var b = d as MainWindowSaveStateBehavior;
if(e.NewValue != null) b.LoadSettings();
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SizeChanged += Window_SizeChanged;
AssociatedObject.StateChanged += Window_StateChanged;
LoadSettings();
}
bool _initialized = false;
private void Window_StateChanged(object sender, EventArgs e)
{
SaveSettings();
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
SaveSettings();
}
private void LoadSettings()
{
if (Config == null) return;
AssociatedObject.Width = Config.WindowWidth;
AssociatedObject.Height = Config.WindowHeight;
AssociatedObject.WindowState = Config.WindowState;
_initialized = true;
}
private void SaveSettings()
{
if (Config == null || !_initialized) return;
Config.WindowState = AssociatedObject.WindowState;
if(AssociatedObject.WindowState == WindowState.Normal)
{
Config.WindowWidth = AssociatedObject.Width;
Config.WindowHeight = AssociatedObject.Height;
}
}
}
In Xaml use the behavior by adding the namespaces
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:b="<the namespace your behavior lives in>"
And then attach the Behavior
<i:Interaction.Behaviors>
<b:MainWindowSaveStateBehavior Config="{Binding Config}" />
</i:Interaction.Behaviors>
You then just have to Load and Save the Config in your DataContext on startup/shutdown.
I liked CodeWarriors answer. I use TwoWay binding to apps settings:
Height="{Binding Source={x:Static p:Settings.Default}, Path=WindowHeight, Mode=TwoWay}"
Width="{Binding Source={x:Static p:Settings.Default}, Path=WindowWidth, Mode=TwoWay}"
Top="{Binding Source={x:Static p:Settings.Default}, Path=WindowTop, Mode=TwoWay}"
Left="{Binding Source={x:Static p:Settings.Default}, Path=WindowLeft, Mode=TwoWay}"
where p - project's properties namespace.
I'm writing a one-window UI for a simple ETL tool. The UI consists of the window, the code behind for the window, a view model for the window, and the business logic. I wanted to provide functionality to the users to save the state of the UI because the content of about 10-12 text boxes will be reused between sessions, but are specific to the user. I figured I could serialize the view model, which contains all the data from the textboxes, and this works fine, but I'm having trouble loading the information in the serialized XML file back into the text boxes.
Constructor of window:
public ETLWindow()
{
InitializeComponent();
_viewModel = new ViewModel();
this.DataContext = _viewModel;
_viewModel.State = Constants.STATE_IDLE;
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
XAML:
<TextBox x:Name="targetDirectory"
IsReadOnly="true"
Text="{Binding TargetDatabaseDirectory, UpdateSourceTrigger=PropertyChanged}"/>
ViewModel corresponding property:
private string _targetDatabaseDirectory;
[XmlElement()]
public string TargetDatabaseDirectory
{
get { return _targetDatabaseDirectory; }
set { _targetDatabaseDirectory = value; OnPropertyChanged(DataUtilities.General.Utilities.GetPropertyName(() => new ViewModel().TargetDatabaseDirectory)); }
Load event in code behind:
private void loadState_Click(object sender, RoutedEventArgs e)
{
string statePath = this.getFilePath();
_viewModel = ViewModel.LoadModel(statePath);
}
As you can guess, the LoadModel method deserializes the serialized file on the user's drive.
I couldn't find much on the web regarding this issue. I know this probably has something to do with my bindings. Is there some way to refresh on the bindings on the XAML after I deserialize the view model? Or perhaps refresh all properties on the view model? Or am I completely insane thinking any of this could be done?
Thanks.
Assuming that your loadState_Click event is on the Window code behind you could try this.
private void loadState_Click(object sender, RoutedEventArgs e)
{
string statePath = this.getFilePath();
this.DataContext = ViewModel.LoadModel(statePath);
}