I'm sure there's something I'm missing here, but a lot of Googling hasn't uncovered it for me. The situation is like this:
We created a custom workflow designer that allows end users to build workflow definitions from various custom activities we define (Review, Submit, Notify, etc). These definitions (Xaml) get saved off to a Db and used to create workflow instances for long running processes in our system. The users can set properties on each of them (e.g. Review has a property argument: AllowedRoles). The problem is, I'm not able to pass those properties on to nested activities.
For example:
Review has an internal activity 'WriteStatus' that needs access to the 'AllowedRoles' property on Review. If 'AllowedRoles' is defined as a Property, WriteStatus can't "see" it to assign it's value. I can change it from a Property to an InArgument, but then I'm not able to map values to and from the property in the designer (these properties should be part of the definition, and not associated with any specific context).
Has anyone faced this issue or have advice on how I could approach the problem differently?
Thanks in advance!
Royce
I was able to get around the property vs InOurArgument problem by converting the XAML activities to code. This allowed me to set the properties on activities in code, and then pass them to inner activities inline. There may be a better way, but it's working out well so far.
public sealed class Test : Activity
{
public string Stuff { get; set; } // CLR Property
public Test()
{
Implementation = () => new WriteLine {Text = Stuff};
}
}
Related
On my first project trying out Caliburn.Micro, I like a lot of the things :-)
One thing I miss (or havn't discovered yet) is how to separate the viewmodel and a command.
CM doesn't support ICommand, as it's way of doing things is superior. I'm sure it's true, so I would love a small push in the right direction to achieve this or perhaps discover a better way.
As I understand you have to put the "Execute" method and "CanExecute" property directly in the viewmodel, named to match the control in the view, to get the magic to work.
I would like to put the "Execute" and "CanExecute" in a different object that is property on the viewmodel and then CM would automatically bind to that object, using the control name and property names as usually.
Repost from the forum on Caliburn Micro, I didn't get any answers so I'm trying my luck here.
You should try to avoid invalidating the Law of Demeter. Therefore, on your view model you can have an execute method, and a CanExecute property (usually calculated), and these can call into the containing model where appropriate, e.g:
public void Save
{
// .. save logic
}
public bool CanSave
{
get
{
return this.model.CanSave ... and other logic etc.
}
}
You must remember to notify a change in the calculated property when the can save state changes, e.g:
public void CodeThatGetsRunWhenAPropertyOfTheModelChanges()
{
this.NotifyOfPropertyChanged(() => this.CanSave);
}
If you have e.g. a Button on your view with x:Name="Save", then Caliburn.Micro will automatically invoke your Save verb on the view model when the button is clicked, and will automatically enable and disable the button when the CanSave property value changes.
To avoid fat ViewModels you also need to avoid fat Views. Caliburn.Micro allows you to compose Views/ViewModels as described in Screens, Conductors and Composition.
The short version is, you can include a "DetailView" and "DetailViewModel" pair in a "MasterView"/"MasterViewModel" shell by defining a DetailViewModel-typed property in MasterViewModel and adding a ContentControl named after it in MasterView. Binding and actions work as usual, so you avoid both fat models/views and routing of commands.
Another option is to bind a MasterView element to a DetailViewModel property or action, by prepending the detail's property to the target's name. I can't find the specific URL yet, so the example is from memory.
Assuming you have the following classes:
public class MasterViewModel:Screen
{
public property DetailViewModel MyDetails{get;set;}
}
and
public class DetailViewModel:Screen
{
public property string SomeText{get;set;}
public void DoTheBoogie(){}
}
You can add a control in you MasterView named 'MyDetails_SomeText' to bind to the DetailViewModel.SomeText. You can also bind to DoTheBoogie the same way.
I prefer to create a separate View though, named DetailView and add a ContentControl named "MyDetails" in MasterView. This results in a cleaner and more modular design
Using EF with Winforms in C#. I’d like to add full custom properties to our entities, using partial classes. All entities already have partial classes with validation stuff and some more so I’d just add the properties that I need. By full property I mean property with getter and setter so not just a computed/readonly property. I want to this mostly to get around working directly with some DB mapped properties which are badly designed or have other problems.
For example, one case would be like this:
// entity class, generated
public partial class Customer
{
public string Spot {get;set}
}
// partial class, manually changed
public partial class Customer
{
public int? xxxSpot
{ get { return Int32.Parse(Spot.Trim()); } // some code omitted
{ set { Spot = value.ToString().PadLeft(5); }
}
So my custom properties will be built around existing, DB mapped properties of the entity. I’d like to use these custom properties like normal ones, ie to bind them to UI controls and so on. I’ve tried one and so far it works great.
Is this a good idea? If not, why ? And what else should I consider when doing this?
You have answered your own question - it works and there is no reason why to not do that. If you want to improve design of your entities you can even try to change visibility of your mapped properties to ensure that other classes must use only your custom properties with additional logic.
I'm trying to figure out to create a behavior that will copy a boolean site configuration value to an output model.
This way I don't have to copy the bool in each action who's view requires it, but can simply add the behavior to the controller actions that need this value.
In some of the older versions of FubuMVC, I believe behaviors could modify the output model after it's left the controller. But I'm not sure how to do this in the more recent versions of FubuMVC (or I've forgotten).
Can anyone give me an example of or point me in the direction of the best practice for copying a site configuration value to an output model?
Let's say I had an output model called HomeViewModel that had a property called FooterText that I wanted loaded from settings object (let's say HomeSettings) that was retrieved from the container (i.e. StructureMap).
The Behavior
My behavior would look something like this:
public class HomeFooterBehavior : BasicBehavior
{
private readonly HomeSettings _settings;
private readonly IFubuRequest _request;
public HomeFooterBehavior(HomeSettings settings, IFubuRequest request)
: base(PartialBehavior.Executes)
{
_settings = settings;
_request = request;
}
protected override DoNext performInvoke()
{
SetupFooter();
return DoNext.Continue;
}
public void SetupFooter()
{
var viewModel = _request.Find<HomeViewModel>().First();
viewModel.HomeFooterText = _settings.FooterText;
}
}
This behavior takes in the HomeSettings object and the IFubuRequest object (both injected dependencies) and then gets the HomeViewModel (output model) from the request and then sets the HomeFooterText property on the output model based on the value from the settings object.
NOTE: I'm assuming that you've already got your HomeSettings object wired up in the container (for example, using the ISettingsProvider stuff built into FubuMVC). If you don't already have this, let me know and I can post some code on how to do that.
Wiring Up The Convention
To wire up the behavior, you'll need to define the convention through an IConfigurationAction, for example:
public class HomeFooterBehaviorConfiguration : IConfigurationAction
{
public void Configure(BehaviorGraph graph)
{
graph.Actions()
.Where(x => x.HasOutput &&
x.OutputType().Equals(typeof(HomeViewModel)))
.Each(x => x.AddAfter(Wrapper.For<HomeFooterBehavior>()));
}
}
This is a real dumb convention for demonstration purposes. In your project, you might make it a little more generic. For example, any output model that has an attribute on it, or implements a specific interface, etc. In fact, you might want to inspect all output models to see if they contain any properties that match a certain criteria (for example, all properties that end with "Settings" - like "FooterSettings" or something).
Don't be afraid to define wide sweeping conventions like this due to performance concerns since all this convention code runs at start-up time and not on every request.
Note the "AddAfter" call and the "Wrapper.For" call. That's the key in that it places your behavior after the controller action is executed, but before the view is rendered.
Now that you have your behavior and your convention defined, it's time to wire it up in your FubuRegistry.
Wiring Up Your Convention in your FubuRegistry
After the call to "Routes." in your FubuRegistry, add a line like this:
ApplyConvention<HomeFooterBehaviorConfiguration>();
Recompile and it should work.
Please let me know if you run into any problems.
I've been looking around for an answer to this, which I can't believe hasn't been asked before, but with no luck I'm attempting here.
I have a signup form which differs slightly based upon what type of participant the requester is. While writing tests for the solution, I realized that all actions did the same things, so I'm attempting to combine the actions into one using a strategy pattern.
public abstract class BaseForm { common properties and methods }
public class Form1 : BaseForm { unique properties and overrides }
....
public class FormX : BaseForm { unique properties and overrides... in all about 5 forms }
Here is the combined action:
[ModelStateToTempData, HttpPost]
public ActionResult Signup(int id, FormCollection collection)
{
uiWrapper= this.uiWrapperCollection.SingleOrDefault(w => w.CanHandle(collection));
// nullcheck on uiWrapper, redirect if null
var /*BaseForm*/ form = uiWrapper.GetForm(); // Returns FormX corresponding to collection.
this.TryUpdateModel(form, collection.ToValueProvider()); // Here is the problem
form.Validate(this.ModelState); // Multi-Property validation unique to some forms.
if (!this.ModelState.IsValid)
return this.RedirectToAction(c => c.Signup(id));
this.Logic.Save(form.ToDomainClass());
return this.RedirectToAction(c => c.SignupComplete());
}
The problem I'm having is that TryUpdateModel binds only the common properties found in BaseForm. My previous code used public ActionResult FormName(int id, FormX form) which bound properly. However, I did some testing and discovered that if I replace var form with FormX form the form binds and everything works, but I'm back to one action per form type.
I'm hoping to find a way to get this to bind properly. form.GetType() returns the proper non-base class of the form, but I'm not sure of how to grab the constructor, instantiate a class, and then throw that into TryUpdateModel. I know that the other possibility is a custom ModelBinder, but I don't see a way of creating one without running into the same FormBase problem.
Any ideas or suggestions of where to look?
I was trying to do something similar to Linq, I was trying to create a class that would inherit some standard fields (ID, etc). I found that the default Linq engine would only use fields from the instantiated class, not from any inherited classes or interfaces.
To construct a Type simply use code like:
var form = Activator.CreateInstance(uiWrapper.GetForm());
I figured it out!
Erik's answer wasn't the solution, but for some reason it made me think of the solution.
What I really want form to be is a dynamic type. If I change this line:
dynamic form = uiWrapper.GetForm();
Everything works :)
On top of that, logic.Save(form.ToDomainClass()) also goes directly to Save(DomainTypeOfForm) rather than Save(BaseDomainForm) so I can avoid the headache there as well. I knew that once I figured out the problem here I could apply the answer in my logic class as well.
I'm writing a Windows application and using a Listbox control. I'm developing with Visual Studio C# 2008 Express Edition.
I've got a data object that looks something like this
public class RootObject
{
public List<SubObject> MySubObjects{ get; set;}
}
I've got a ListBox on my form, and also a property "MyRootObject" which, obviously, holds a RootObject. When the control is initialized, I set:
_listBox.DataSource = MyRootObject.MySubObjects;
Now, when the form loads, I debug and see that the DataSource is being set correctly. But nothing is displayed. I've overridden SubObject's ToString() method and it's not even being called. I tried setting _listBox.DisplayMember to a property of SubObject just to see if there was some problem there, but still nothing. I tried calling _listBox.Update() and _listBox.Refresh() after setting the DataSource, but still no love. The DataSource has all the data... it's just refusing to display it.
So while debugging, I wondered WTF and I decided to just do
_listBox.DataSource = new List<SubObject>{ new SubObject(), new SubObject() };
Sure enough, this worked, and I see two things listed in my listbox.
So then, really curious, I decided to try copying the list of objects and putting that in the listbox, like so:
_listBox.DataSource = MyRootObject.MySubObjects.ToArray();
This works! And it's a workaround to my problem for now... but a very annoying one. Does anyone know why I need to basically copy the list of objects like this to get it to work, rather than just setting the _listBox.DataSource = MyRootObject.MySubObjects; ? Again, the DataSource has all the right data either way after setting it... it's just when it's copied data, it actually displays, and when it's not, it's not displayed.
((CurrencyManager)_listBox.BindingContext[_listBox.DataSource]).Refresh();
Sux0r I know, but this works.
(originally found answer here)
Off the top of my head, this is because the ListBox.DataSource property must contain something that implements the IList interface. Your generic List<SubObject> does not implement IList; it implements IList<T> (in the System.Collections.Generic namespace). Array objects, on the other hand do inherit from IList, so handing the data in via that kind of object works.
You could try pulling an Enumerator (which also implements IList) out of your List<SubObject> object and plug that in. If it works, then the issue I've described is your problem. If it doesn't, then I'm talking out of my hat.
If this is indeed the issue, I am surprised that shoving in an unsupported object doesn't throw an exception.
so far that i know, whenever you want to set a collection to a
[ComboBox,ListBox].DataSource
you have to set the DisplayMember and ValueMember. DisplayMember and ValueMember are filled with the property name of the Class in the collection that is assigned to the listbox/combobox. Ex.
//Populate the data
RootObject root = new RootObject();
root.MySubObjects.Add(new SubObject("1", "data 1"));
root.MySubObjects.Add(new SubObject("2", "data 2"));
//Assign data to the data source
_listBox.DisplayMember = "DisplayProperty";
_listBox.ValueMember = "ValueProperty";
_listBox.DataSource = root.MySubObjects;
root.MySubObjects returns List of SubObject, and SubObject has to have properties called DisplayProperty and ValueProperty, ex.
public class RootObject
{
public List<SubObject> MySubObjects { get; set; }
}
public class SubObject
{
public string ValueProperty { get; set; }
public string DisplayProperty { get; set; }
}
I think you have to call Bind method after assigning to the list box data source
something like _listBox.DataSource.bind()
and you will have your listbox disappeared
you could try and use a BindingSource
( http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx )
inbetween the Listbox and your collection. Bindingsources handles a bunch of stuff and also includes Suspend/ResumeBinding properies that can be useful when updating the list
you could also try out wpf as its databinding is far superior to that of winforms :) but maybe thats not possible in your case
I believe you need to call _listbox.DataBind(); after assigning the datasource.
However, I've never used a property as a datasource before, I've only used methods. Have you tried changing your property to a method to see if that's the problem?
You could try
_listBox.DataSource = new BindingList<SubObject> (MyRootObject.MySubObjects);
instead. BindingList implements some more interfaces than List, which are essential for DataBinding.
I have a special rule for problems like this that I always try to remember before wasting an entire day hammering on it (believe me, I've spent many days hammering!). The rule is: when system behavior is really strange, the underlying cause must be very stupid. As intelligent programmers, we have a tendancy to search for esoteric causes or bugs in framework code to explain our problems. Sometimes the answer is that we were in a hurry and made a careless mistake that we haven't yet caught.
To quote Sherlock Holmes, "when you've removed the impossible, then whatever remains, no matter how improbable, must be the truth". In this case, the improbable truth is that the MySubObjects property of MyRoot object is null.