I am using Dataform to show a object in my Silverlight application. It is a simple Input page which where you can input value and the SAVE button at the bottom submits the information to the database.
My object contains 7 data fields, out of which I need to show only 5 of them. The other two data fields are added in the database directly.
My silverlight application communicates to the database via WCF services.
So, the question is can I filter data fields on the dataform?
If you are Auto-generating the DataForm, you can use
[Display(AutoGenerateField=false)]
public string SomeProperty {get;set;}
This attribute was previously called Bindable in the SL3 beta, and has since changed in the RTM release. More info here
Following is the snippet from xaml file
dataFormToolkit:DataForm
x:Name="dataForm"
CommitButtonContent="Save"
CancelButtonContent="Cancel"
AutoEdit="True"
AutoGenerateFields="False"
Following is the snippet from xaml.cs file
public CreateProduct()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(CreateProduct_Loaded);
}
private void CreateProduct_Loaded(object sender, RoutedEventArgs e)
{
ServiceReference.Product model = new ServiceReference.Product();
dataForm.CurrentItem = model;
}
Related
I am trying to use the built-in data binding to load and save workflow definitions to a database using the rehosted workflowdesigner class, WPF and MVVM. My xaml.cs file programatically creates the workflow designer and sets the WorkflowDEsigner.View to a Tab Content control.
I'm looking for a way to data bind the a property of the workflow designer to my view model object. That way, when the user creates a workflow definition and hits save, my viewmodel will (via databinding) have the string representation of the workflow definition so that it can be persisted to a database. And when a user clicks Load, a workflow definition will be queried from the database, and hydrated into the MVVM object and then databound to the workflowdesigner for display.
The WorkflowDesigner.Text property doesn't get populated until WorkflowDesigner.Save() is called which saves the definition to a file. I definitely don't want to do that.
Currently I'm rolling my own by hooking the WorkflowDesigner.ModelChanged event and in that event handler, get the text and then manually set the view model. Is there a pure databinding way to accomplish this?
//initialization of the designer
m_objWFWorkerDesigner = new WorkflowDesigner();
m_objWFWorkerDesigner.Context.Services.GetService<DesignerConfigurationService>().AnnotationEnabled = true;
m_objWFWorkerDesigner.Context.Services.GetService<DesignerConfigurationService>().TargetFrameworkName = new
System.Runtime.Versioning.FrameworkName(".NET Framework", new Version(4, 5));
m_objWFWorkerDesigner.Load(new System.Activities.ActivityBuilder());
m_objWFWorkerDesigner.ModelChanged += m_objWFWorkerDesigner_ModelChanged;
void m_objWFWorkerDesigner_ModelChanged(object sender, EventArgs e)
{
((RehostedWorkflowDesignerVM)this.DataContext).WorkerWorkflow = GetActivityString(sender);
}
private static string GetActivityString(object sender)
{
ActivityBuilder activityBuilder = (ActivityBuilder)((WorkflowDesigner)sender).Context.Services.GetService<ModelTreeManager>().Root.GetCurrentValue();
StringBuilder sb = new StringBuilder();
StringWriter tw = new StringWriter(sb);
XamlWriter xw = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(tw, new XamlSchemaContext()));
XamlServices.Save(xw, activityBuilder);
return sb.ToString();
}
WorkflowDesigner and its surrounding controls (toolbox, properties editor, etc.) are not MVVM-friendly so you won't be able to bind data to them the proper way.
What I've done in FlowStudio was to encapsulate those controls inside a truly WPF control which uses dependency properties to enable databinding through XAML.
I've started by doing it to ToolboxControl. You can check it here.
Most WPF/EF tutorials only cover databinding in one window. However, in reality data gets displayed across many windows. You often display a record in the first window and dig deeper in related details in the next windows.
So, this also is the case in my scenario. Here you can see my data structure and the ui. Actually I am not dealing with Customers and Invoices, but the structure is the same. (My concrete questions are at the very end.)
In the InvoicesWindow I can select an Invoice and press "Show Invoice". That opens a CustomerWindow displaying Customer details and his invoices. The right invoice is pre-selected. To each Invoice displayed in the CustomerWindow I can add Items or edit them. This is done in a seperated window called "ItemWindow". Editing the DataGrids is not an option. They are set to ReadOnly.
Here is the code of the wpf-window classes (I only have done displaying data yet, not saving):
Invoices Window:
public partial class InvoicesWindow : Window
{
private MyEntities context = new MyEntities();
public InvoicesWindow ()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CollectionViewSource invoicesViewSource = (CollectionViewSource)FindResource("invoicesViewSource");
invoicesViewSource.Source = context.Invoices;
}
private void ShowInvoice_Click(object sender, RoutedEventArgs e)
{
Invoice selectedInvoice = (Invoice)InvoicesDataGrid.SelectedItem;
var customerWindow = new CustomerWindow(selectedInvoice);
customerWindow.ShowDialog();
}
}
Customer Window:
public partial class CustomerWindow : Window
{
private MyEntities context = new MyEntities();
private Invoice selectedInvoice;
public CustomerWindow()
{
InitializeComponent();
}
public CustomerWindow (Invoice selectedInvoice)
{
InitializeComponent();
this.selectedInvoice = selectedInvoice;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Set the data
CollectionViewSource customerViewSource = (CollectionViewSource)FindResource("customerViewSource ");
customerViewSource.Source = context.Customers.Where(p => p.id == selectedInvoice.Customer.id);
//Select the right invoice
CollectionViewSource customerInvoicesViewSource = (CollectionViewSource)FindResource("customerInvoicesViewSource ");
customerInvoicesViewSource.Items.MoveCurrentTo(((ObjectSet<Invoice>)customerInvoicesViewSource.Source).Where(p => p.id == selectedInvoice.id).SingleOrDefault());
}
private void EditItem_Click(object sender, RoutedEventArgs e)
{
Item selectedItem = (Item)ItemsDataGrid.SelectedItem;
var itemWindow = new ItemWindow((IQueryable<Customer>)(customerViewSource.Source),selectedInvoice,selectedItem);
itemWindow.ShowDialog();
}
}
Item window:
public partial class ItemWindow : Window
{
private Invoice _selectedInvoice;
private Invoice _selectedItem;
private IQueryable<Customer> _customers;
public ItemWindo()
{
InitializeComponent();
}
public ItemWindow(IQueryable<Customer> customers, Invoice selectedInvoice, Item selectedItem)
{
InitializeComponent();
this._customers = customers;
this._selectedInvoice = selectedInvoice;
this._selectedItem = selectedItem;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Set the data
CollectionViewSource customerViewSource = (CollectionViewSource)FindResource("customerViewSource");
invoicesViewSource.Source = _customers;
//Select the right invoice
CollectionViewSource customerInvoicesViewSource = (CollectionViewSource)FindResource("customerInvoicesViewSource ");
customerInvoicesViewSource.Items.MoveCurrentTo(_selectedInvoice);
//Select the right item
CollectionViewSource customerInvoicesItemsViewSource = (CollectionViewSource)FindResource("customerInvoicesItems");
customerInvoicesItems.Items.MoveCurrentTo(_selectedItem);
}
}
I wrote the code out of my mind. So, maybe some casts are missing and some methods are mis-spelled. I hope I got the right type with "ObjectSet", it also could be "ObjectCollection" or something like that.
The XAML was created widely with assistance of VS2010 like in this video: http://msdn.microsoft.com/de-de/data/ff806174.aspx
So, finally my questions ;)
Is the design of binding I applied correct?
In CustomerWindow I create a new context.
Between CustomerWindow and ItemWindow I just pass the data of the same context and select the current item manually.
In CustomerWindow I use a ObjectSet (or ObjectCollection, I'm not sure about the type anymore) with a single entry as Source for the customersCollectionViewSource. This works fine. However, there is no need for a collection, because I only edit a single Customer. I did not manage to set a single Customer as Source. I didn't know how to adjust the view source which was generated by VS2010.
I haven't done saving yet. But I think I am going to run into problems due to my design between CustomerWindow and ItemWindow. Maybe you can give me some advice here.
When the "Apply"-Button in ItemWindow gets pressed, the Item data should be updated in DB. But not the Customer- and Invoices-related data in the CustomerWindow underneath.
The DataGrid of Items in CustomerWindow should get updated, when closing the ItemWindow. But not the rest of the fields in the CustomerWindow, since here could have been data changed before opening the ItemWindow.
The only solution for me to overcome that "synchronisation problem": The User is forced to press "Apply" in the CustomerWindow before he can press the "New Item" or "Edit Item", if there have been any changes. (Kinda like the "window resolution control" of windows 7 when working with two monitors) But this is not too user friendly.
A cleaner design would be to use the MVVM design pattern.
Inject the view model into the window's context and bind the view model to either a collection of entities or a single entity, bind in the xaml to properties in the view model(s) and use commands implemented in the view model for actions e.g. add new, delete.
The windows shouldn't be aware of the context.
If you have a list view model + window and a details window (preferably with a view model), then the list view model should pass the selected item to the details view model (or window) as the context.
If the windows are not open at the same time or do not have related objects, then their views models should not share a database context, otherwise, in order for the changes to be reflected easily between the windows, they will have to share the database context.
I want some suggestions to implement this functionality with a neat design and without any code replication. I have an application with many views and grid control in most of the views. I need to add an export functionality (export records to excel).The grid control supports this OOB, just need to call 'Grid.Export()'. I am planning a UI button on the side of every grid and call this method.
So, obviously I need to write the code in code-behind only since I need the control's instance to invoke the method. But, I like to keep the code in one place and somehow invoke the code from all Xamls. (all WPF views).
One technique is to write a BaseView class and derive all Views from this.
But would like to know if WPF suppots any techniques by which I can achieve this. (behaviours etc..?)
Thanks,
Mani
Create a UserControl that includes both the datagrid and the export button. In effect, make it part of the grid itself.
Use this UserControl instead of the default datagrid in all of your views, and you're done.
Furthermore, if you ever have to modify the look and feel of your button or its behaviour, you have only one place in which to change it, and it will be updated in all of your views.
One of solutions is to use WPF routed command.
Note: I wrote this answer with the assumption that your "View" is a subclass of Window class.
First, add a custom routed command to your project.
public static class MyCommands
{
private static readonly RoutedUICommand exportCommand = new RoutedUICommand("description", "Export", typeof(MyCommands));
public static RoutedUICommand ExportCommand
{
get
{
return exportCommand;
}
}
}
In each View, set your custom command to Button.Command and bind a target object to Button.CommandTarget.
<Button Command="local:MyCommands.ExportCommand" CommandTarget="{Binding ElementName=dataGrid1}">Export</Button>
Firnally, in your Application class (named App by default), register a command binding between your custom command and Window.
public partial class App : Application
{
public App()
{
var binding = new CommandBinding(MyCommands.ExportCommand, Export, CanExport);
CommandManager.RegisterClassCommandBinding(typeof(Window), binding);
}
private void Export(object sender, ExecutedRoutedEventArgs e)
{
// e.Source refers to the object is bound to Button.CommandTarget.
var dataGrid = (DataGrid)e.Source;
// Export data.
}
private void CanExport(object sender, CanExecuteRoutedEventArgs e)
{
// Assign true to e.CanExecute if your application can export data.
e.CanExecute = true;
}
}
Now, App.Export is invoked when user click a button.
Sample is available here.
Greetings! Am enjoying using MVVM light -great framework - has made my life much easier, and has removed a number of barriers that were proving difficult to overcome....
Question:
I am attempting to setup a custom dialog box for editing messages users send to each other. I am attempting to construct a silverlight custom dialog box using the ChildWindow object using the MVVM framework.
Was wondering if there were any suggestions as to how this might be accomplished
Following the dialog MVVM sample code I found here: http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338 I got stuck because the ChildWindow dialog object in Silverlight is async, and has a different Result class.
So - the Basic idea I have now is using the view model of the class (in this case the Matrix.MessageViewModel) to create an instance of the custom dialog box, send it through the Messenger.Send<>, process the registered message in the view to display the dialog, then have the ChildWindow dialog box's Save button handler fire a Messenger.Send with the modified contents that is then stored using the Save method on the viewmodel...
Seems a bit round-about - so wanted to make sure there wasn't a cleaner way....
Relevant code bits:
view model:
messageDialogBox = new MessageEditorDialog(
selectedMessage, this.SelectedSiteId, this.LoggedOnEmployee.Id, this.Projects);
DialogMessage editMessage = new DialogMessage(
this, messageDialogBox,"Edit Message", DialogMessageCallback);
Messenger.Default.Send(editMessage);
View:
public ViewHost()
{
InitializeComponent();
Loaded += new RoutedEventHandler(ViewHost_Loaded);
if (!ViewModelBase.IsInDesignModeStatic)
{
// Use MEF To load the View Model
CompositionInitializer.SatisfyImports(this);
}
ApplicationMessages.IsBusyMessage.Register(this, OnIsBusyChange);
Messenger.Default.Register<DialogMessage>(this, msg => ShowDialog(msg));
}
private void ShowDialog(DialogMessage msg)
{
MessageEditorDialog myDialog = (MessageEditorDialog) msg.Target;
myDialog.Show();
}
Dialog Save:
private void ButtonSave_Click(object sender, RoutedEventArgs e)
{
Messenger.Default.Send<Message>(
this.MessageItem, CommandMessages.MessageTypes.MessageSave);
}
This ties back into the ViewModel, that has a Messenger.Default.Register<> watching for the CommandTypes.MessageSave which routes the resulting MessageItem to the model for storage.....
That's pretty darn close to what I'd do, except there are a couple of things I do differently.
I'd have a view model for my dialog view, and move the messaging logic to it rather than the view's code behind.
I'd use a Save command in my view model, and bind the ButtonSave to that command. That moves the save logic to the view model instead of the code behind of your view.
You're using a different message when the save button is clicked. Also, you're not using the DialogMessage's callback. Assuming you change to using a Save command, you could save the message in a private member in the view model, then use message's callback when the user saves.
You may want to think about re-using the dialog view, or ensuring that the view is being cleaned up correctly so you don't end up with a memory leak.
Here's the changes I'd make to your view model following suggestions 2 & 3.
public class MessageEditorDialogViewModel : ViewModelBase
{
private DialogMessage _dialogMessage;
public RelayCommand SaveCommand { get; private set; }
public DialogMessage Message { get; set; }
public MessageEditorDialogViewModel()
{
SaveCommand = new RelayCommand(SaveCommandExecute);
}
private SaveCommandExecute()
{
Message.Execute();
}
}
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);
}