I have just fired up a WPF project and I want to use Caliburn.Micro.
I have a button
<Button Content="Button" Name="AppendData">
and in my ViewModel I have a method void AppendData(){..}
It doesn't work! There is no binding between the two! But when I do this
<Button Content="Button" cal:Message.Attach="AppendData()">
it suddenly works. What can be the cause of this?
Edit:
I have created a test application where the conventions doesn't work: http://ge.tt/8sNsu201?c
You can make it work, by replacing the controls in MyView with
<Button cal:Message.Attach="SetText()" Content="Button" HorizontalAlignment="Left" Margin="106,153,0,0" VerticalAlignment="Top" Width="75"/>
<Label Content="{Binding Text}" HorizontalAlignment="Left" Margin="124,104,0,0" VerticalAlignment="Top"/>
After taking a look at your source code, I noticed a major mistake which is causing all of this confusion:
public MyView()
{
InitializeComponent();
DataContext = new MyViewModel(); // SOURCE OF TROUBLE
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
In Caliburn.Micro you don't set the DataContext for your view manually like that, instead you let Caliburn.Micro use its conventions to find the appropriate view for your view-model, then it will bind the two together (by setting the view-model as the DataContext of the view), after that it will apply a number of conventions to make everything work correctly.
Explaining why using cal:MessageAttach() would work and directly using AppendData won't work would take a lot of explanation because it seems you don't know the basics of CM.
So I advise you to take a look at the documentation wiki first and go through the first 5 articles at least, then here is a hint that will help you discover why the first method worked and the second didn't:
Message Bubbling
Because this would expand the comments maximum length, I write it as an answer.
As you mentioned in your answer, doing DataContext = new MyViewModel() is a kind of code smell in CM. If you want to hook up it manually in your view, this would be the right way (view first). Check out the CM documentation regarding this one though, because I think there might be missing something:
var viewModel = new MyViewModel();
var view = this;
ViewModelBinder.Bind(viewModel, view, null);
You can accomplish this in the XAML of your view, either. Add the following into the UserControl tag of your view (view first, as well):
xmlns:cal="http://www.caliburnproject.org"
cal:Bind.Model="MyViewModel"
View model first would be done quite the same, in case you are not willing to use the default behavior you described in your answer:
xmlns:cal="http://www.caliburnproject.org"
cal:View.Model="MyViewModel"
I am not sure, but I think you have to add an explicitly named export contract to your view model, if you want to use View.Model or Bind.Model, but it might be it works without as well. Try it out:
[Export("MyViewModel", typeof(MyViewModel))]
public class MyViewModel : Screen
{
// ...
}
Design time views have nothing to do with view first or view model first though!
Design-time view support is accomplished as follows:
xmlns:cal="http://www.caliburnproject.org"
d:DataContext="{d:DesignInstance viewModels:MyViewModel, IsDesignTimeCreatable=True}"
cal:Bind.AtDesignTime="True"
I am currently not able to test all those things, so I hope there are not any mistakes!
Related
If I have a button in my View named, say, Save, then I can add a Save property to my ViewModel, and Caliburn.Micro will automatically bind it to my button's Content. For example:
public string Save { get { return StringResources.Save; } }
Or I can add a Save method to my ViewModel, and Caliburn.Micro will execute that method when the button is clicked. For example:
public void Save() {
Document.Save();
}
But what if I want to do both? C# doesn't let me declare a method and a property with the same name. Can I use conventions to both set the button's Content and the action to perform when it's clicked?
(I know I can manually bind one or the other, but I'd rather use conventions if it's practical.)
This is a common need, so you'd think it would be built into Caliburn.Micro, but it doesn't seem to be. I've seen some code that extends the conventions to support this (and I'll post it as an answer if nothing better comes along), but it's a workaround with some bizarre quirks -- so I'd like to hear if anyone else has made this work more cleanly.
Note: I did see this similar question, but it seems to be about whether this is a good idea or not; I'm asking about the mechanics. (I'll reserve judgment on whether it's a good idea until I've seen the mechanics. (grin))
Quick and dirty
<Button x:Name="Save"><TextBlock x:Name="SaveText"></TextBlock></Button>
This is probably a very simple question, but at this time I have myself so confused I can't see the answer. Simply put, I have a window that contains a content control. I'm using Caliburn.Micro's conventions to "locate" the view.
The window looks like this:
<Window x:Class="Views.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox/>
<ContentControl x:Name="MyViewModel" Height="Auto" Background="Blue"/>
</Grid>
</Window>
The view itself is successfully found, and the screen displays as I expected. However, MyViewModel needs to make a service call to get information based on what is typed into the text box.
So, what I can't seem to figure out is how one would pass that information from the text box to the view model. I've thought of several options, but they all seem to be too much work which makes me think that I'm missing something simple.
Thanks a lot
Like you said there are a number of things you can do:
You could expose a property on MyViewModel and set it within
MainWindowView.
You could use the EventAgregator, publish an event from the
MainWindowView and subscribe to that event from MyViewModel.
Using MEF you could inject a shared resource between the two
ViewModels, set it in MainWindowViewModel, and be able to access it
from MyViewModel.
The application runs fine but i could not see my design in the designer view.
It says Cannot find resource named 'Locator'. Obviously, i did not change anything in the code, i just did the data binding using the data binding dialog...
anyone facing the same problem?
There are two known occurrences where this can happen.
If you change to Blend before you built the application, the DLLs are not available yet and this error can be seen. Building the application solves the issue.
There is a bug in Expression Blend where, if you are placing a user control in another user control (or Window in WPF), and the inner user control uses a global resource, the global resource cannot be found. In that case you will get the error too.
Unfortunately I do not have a workaround for the second point, as it is a Blend bug. I hope we will see a resolution for that soon, but it seems to be still there in Blend 4.
What you can do is
Ignore the error when working on the outer user control. When you work on the inner user control, you should see the design time data fine (not very satisfying I know).
Use the d:DataContext to set the design time data context in Blend temporarily.
Hopefully this helps,
Laurent
I've come up with a reasonably acceptable workaround to this problem since it doesn't appear to have been fixed in Blend 4:
In the constructor for your XAML UserControl just add the resources it needs, provided you're in design mode within Blend. This may be just the Locator, or also Styles and Converters as appropriate.
public partial class OrdersControl : UserControl
{
public OrdersControl()
{
// MUST do this BEFORE InitializeComponent()
if (DesignerProperties.GetIsInDesignMode(this))
{
if (AppDomain.CurrentDomain.BaseDirectory.Contains("Blend 4"))
{
// load styles resources
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri(System.IO.Path.Combine(Environment.CurrentDirectory, "Resources/Styles.xaml"), UriKind.Absolute);
Resources.MergedDictionaries.Add(rd);
// load any other resources this control needs such as Converters
Resources.Add("booleanNOTConverter", new BooleanNOTConverter());
}
}
// initialize component
this.InitializeComponent();
}
There may be some edge cases, but its working OK for me in the simple cases where before I'd get a big red error symbol. I'd LOVE to see suggestions on how to better solve this problem, but this at least allows me to animate user controls that otherwise are appearing as errors.
You could also extract out the creation of resources to App.xaml.cs:
internal static void CreateStaticResourcesForDesigner(Control element)
{
if (AppDomain.CurrentDomain.BaseDirectory.Contains("Blend 4"))
{
// load styles resources
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri(System.IO.Path.Combine(Environment.CurrentDirectory, "Resources/Styles.xaml"), UriKind.Absolute);
element.Resources.MergedDictionaries.Add(rd);
// load any other resources this control needs
element.Resources.Add("booleanNOTConverter", new BooleanNOTConverter());
}
}
and then in the control do this BEFORE InitializeComponent():
// create local resources
if (DesignerProperties.GetIsInDesignMode(this))
{
App.CreateStaticResourcesForDesigner(this);
}
Note: At some point in time this stopped working for me and I ended up hardcoding the path to the Styles.xaml because I got frustrated trying to figure out which directory I was in.
rd.Source = new Uri(#"R:\TFS-PROJECTS\ProjectWPF\Resources\Styles.xaml", UriKind.Absolute);
I'm sure I could find the right path with 5 minutes work, but try this if you're at your wits end like I was!
In MyUserControl.xaml, instead of:
DataContext="{Binding Main, Source={StaticResource Locator}
use:
d:DataContext="{Binding Main, Source={StaticResource Locator}
where "d" has been previously defined as:
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
The reason and workaround explained here
http://blogs.msdn.com/b/unnir/archive/2009/03/31/blend-wpf-and-resource-references.aspx
Look at (b) part of the post.
I had a similar problem with a user control resource.
I added this in my usercontrol xaml code:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/GinaControls;component/Resources/GinaControlsColors.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Where GinaControls is the namespace where the control class is declared and /Resources/GinaControlsColors.xaml is the project folder and xaml resource file name.
Hope this helps.
Just add this in your App.xaml.cs at the very beginning
here's my piece of code
[STATThread()]
static void main(){
App.Current.Resources.Add("Locator", new yournamespace.ViewModel.ViewModelLocator());
}
public App(){
main();
}
Make sure the Blend has opened the entire solution and NOT just the single project containing the views. I was right-clicking in Visual Studio and selecting Open In Expression Blend. To my surprize, Blend could not find the solution file, so it only opened the single project.
When I realized this, I launched Blend directly, pointed it to the solution file, and then Blend was able to find the ViewModelLocator in my view.
I'm having a right barney getting my head around how everything fits together using the MVVM pattern. It all seems quite simple in practice but trying to implement it I seem to be breaking various other rules that I try to code by.
Just as a side note, I'm trying to implement the pattern using Flex, not Silverlight or WPF, so if anyone can come with good reasons why this shouldn't be done, then I'd like to hear them.
I have a problem where I have several views. Sometimes I have to display two views on the page at the same time; sometimes I switch back to a single view. In my normal Flex brain I would have a main view with a code-behind which contained all my other views (equally with code-behinds). That main view would then do the switching of the other individual views.
When I try to implement this in MVVM I'm trying to stick to the principles of MVVM by using binding which decouples my Views from the ViewModels. Let's say I create a ViewModel for application-wide state and my ApplicationView binds to that data and does all the switching of the sub views.
Now, where should I create my view models for my subviews? I've tried inside the ApplicationView -- that didn't seem right. And then I've tried outside of the application view and passing and instance of it into the ApplicationView and then my sub models a bind to it. Am I missing something? None of these methods seem to fit the whole point of trying to decouple this.
Any good books or links that explain this problem would be much appreciated.
Cheers,
James
The approach you are referring to is ViewModel composition. Its where you have multiple complex view parts that need to bind to their own ViewModel entity. The approach entails constructing a root ViewModel with properties for each child ViewModel. Then the root View is bound to the root View Model and each View (whether displayed or collapsed) is bound to the corresponding property on the root ViewModel.
The ViewModel would look like this:
public class RootViewModel
{
ChildViewModelA ChildA { get; set; }
ChildViewModelB ChildB { get; set; }
}
The View would look like this:
<Grid>
<ChildViewA DataContext="{Binding ChildA}" />
<ChildViewB DataContext="{Binding ChildB}" />
</Grid>
You could also implement this in away to allow yourself to select an active workspace.
The ViewModel would look like this:
public class RootViewModel
{
public List<ViewModel> ChildWorkspaces { get; set; }
public ViewModel ActiveWorkspace { get; set; }
public RootViewModel()
{
ChildWorkspaces.Add(ChildViewModelA);
ChildWorkspaces.Add(ChildViewModelB);
}
}
The View would look like this:
<Grid>
<Grid.Resources>
<DataTemplate DataType="ChildViewModelA">
<ChildViewA />
</DataTemplate>
<DataTemplate DataType="ChildViewModelB">
<ChildViewB />
</DataTemplate>
</Grid.Resources>
<ContentControl Content="{Binding ActiveWorkspace}" />
</Grid>
This will result in the appropriate visual representation being selected implicity based on the type of the actual object stored in ActiveWorkspace.
Pardon my response was in WPF. I tried my hardest to not get caught up in the syntax of it all :-)
As you can see the plurality of "ViewModel" can be ambiguous. Often times we find the need to construct multiple sub-entities to structure the ViewModel appropriately. But all ViewModel entities would be somewhere within the root View Model object.
When implementing MVVM in WPF, I prefer to infer what visual element to apply data context implicitly (as illustrated in the later half of this response). In more complex scenarios I prefer to use a DataTemplateSelector to conduct that decisioning. But in super simple cases you can explicitly apply DataContext either imperatively in C#/ActionScript or declaratively through bindings.
Hope this helps!
I've seen variants of the MVVM approach used on a couple different Flex projects, but I haven't seen an approach that feels perfectly right to me. That said, I think using Presentation Models makes testing in Flex a lot easier, so I'm pretty sure that there will start to be more applications designed around this pattern.
The easiest approach I've seen to implementing MVVM in Flex is to place the individual ViewModels within the application Model / ModelLoactor. The ModelLoactor contains any global data and also serves as an accessor to all ViewModels. ApplicationViews can then bind to their particular ViewModel through the ModelLocator, while ViewModels can be updated both through Commands and through bindings to their parent ModelLocator. One benefit of this approach is that all of the data logic is localized; of course, this could also be seen as a drawback, with the central ModelLocator being a touch brittle due to its hard coded references to all ViewModels.
I've seen cleaner approaches work by using the Mate framework. Mate allows for a much more decentralized injection of ViewModels into the appropriate ApplicationViews. (I suppose this could also be accomplished with Swiz, I'm just not as familiar with that framework). With Mate, each ApplicationView has its ViewModel injected via a Map. What's cool with this approach is how ViewModels can be updated using an EventMap (the Mate version of a FrontController). Essentially, your ApplicationViews will dispatch events that are handled by one or more EventMaps, and these Maps can then make changes to one or more of the ViewModels. This approach allows for a user gesture or event from one ApplicationView to change the state of several ViewModels at once. In addition, because this logic is extracted into Mate's EventMaps, it's very easy to change how events are handled or which ViewModels are changed. Of course, the major drawback of this approach is that you're committing to using Mate as a framework, which may not be an option depending on the requirements of the project.
I hope that helps!
I wanted to share a comparison I wrote up of MVVM (Silverlight) vs PresentionModel (Flex). It shows how the two implementations of the same pattern differ/compare:
http://houseofbilz.com/archives/2010/12/29/cross-training-in-silverlight-flexmvvm-vs-presentation-model/
First and foremost, I apologize for any vagueness in this question. At this point, I'm simply trying to get some new ideas of things to try in order to diagnose this bug.
Anyway, the problem I'm having is with an application that's using a custom moduleloader. That moduleloader has been compiled into an swc and the moduleloader is being instantiated via its namespace. This all works perfectly fine. The problem I'm encountering is specific to mx:button controls used within modules. For whatever reason, their labels are being truncated so, for example, Sign In is showing up with an ellipsis, as Sign ...
After quite a bit of fooling around I have been able to establish the following:
This problem only seems to occur within modules. If a button control is used in the main mxml, the label does not get truncated.
The button control whose label is being truncated does not have a width specified (setting its width to 100% or a specific pixel width doesn't fix the issue)
The button control is using the default padding (messing with the padding by setting left and right to 5 or any other value doesn't help matters either).
We are not using any embedded fonts so I've ruled that out as a possibility as well.
mx:CheckBox and mx:LinkButton are equally impacted by this problem although mx:CheckBox also seems to not want to show its checkbox, it just shows the truncated label.
A potential side affect of this is that attaching a dataprovider to mx:ComboBox causes the combobox control to throw a drawing error but I'm not entirely certain that it's related to the above problem.
One interesting thing I did find while perusing the net for an answer was a mention of fontContext and its relationship to IFlexModuleFactory. There's no specification for fontContext within our implementation of moduleloader so I'm not entirely certain if this could be the issue. In any case, if anyone has any ideas, it would be hugely appreciated. On the other hand, if you know exactly what ails me and can provide me with an answer, I might just wet myself with excitement. It's late. I'm tired. I NEED my Flex app to play nice.
Thanks in advance,
--Anne
Edit: To clarify what I'm looking for with this question, I really just need to know the following:
Could this issue be caused by a namespace conflict?
What else can potentially override the default behavior of labels if no CSS has been implemented?
Has anyone encountered a problem with inheritance being lost while using a custom implementation of moduleloader?
Has anyone encountered this problem or a similar problem with or without using moduleloader?
I'm not sharing any code with this question simply because I'd have to share the entire application and, unfortunately, I can't do that. Again, I'm not looking for the end all, be all solution, just some suggestions of things to look out for if anyone has any ideas.
I've been dealing with this issue myself, off and on and in various forms, for a year, and while I haven't figured out just what's causing it yet, there's clearly a mismeasurement happening somewhere along the line.
What I have been able to to, though, is work around it, essentially by subclassing button-type controls (in my case, Button, LinkButton, PopUpButton, et. al.) and assigning their textField members instances of a UITextField extension whose truncateToFit element simply returns false in all cases:
public class NonTruncatingUITextField extends UITextField
{
public function NonTruncatingUITextField ()
{
super();
}
override public function truncateToFit(s:String = null):Boolean
{
return false;
}
}
The custom component just extends Button (or whatever other button-type control is the culprit -- I've created a half-dozen or so of these myself, one for each type of control), but uses a NonTruncatingTextField as its label, where specified by the component user:
public class NonTruncatingButton extends Button
{
private var _truncateLabel:Boolean;
public function NonTruncatingButton()
{
super();
this._truncateLabel = true;
}
override protected function createChildren():void
{
if (!textField)
{
if (!_truncateLabel)
textField = new NonTruncatingUITextField();
else
textField = new UITextField();
textField.styleName = this;
addChild(DisplayObject(textField));
}
super.createChildren();
}
[Inspectable]
public function get truncateLabel():Boolean
{
return this._truncateLabel;
}
public function set truncateLabel(value:Boolean):void
{
this._truncateLabel = value;
}
}
... so then finally, in your MXML code, you'd reference the custom component thusly (in this case, I'm telling the control never to truncate its labels):
<components:NonTruncatingButton id="btn" label="Click This" truncateLabel="false" />
I agree it feels like a workaround, that the component architecture ought to handle all this more gracefully, and that it's probably something we're both overlooking, but it works; hopefully it'll solve your problem as you search for a more definitive solution. (Although personally, I'm using it as-is, and I've moved on to other things -- time's better spent elsewhere!)
Good luck -- let me know how it works out.
I've used the custom button and link button class solutions and still ran into problems - but found a workaround that's worked every time for me.
Create a css style that includes the font you'd like to use for you label. Be sure to check 'embed this font' right under the text selection dropdown. Go back and apply the style to your button (or your custom button, depending on how long you've been bashing your hear against this particular wall), and voila!
Or should be voila...
I just came across this issue and solve it this way:
<mx:LinkButton label="Some label"
updateComplete="event.target.mx_internal::getTextField().text = event.target.label"
/>;
I've had some success preventing Flex's erroneous button-label truncation by setting labelPlacement to "bottom", as in:
theButton.labelPlacement = ButtonLabelPlacement.BOTTOM;
Setting the label placement doesn't seem to help prevent truncation in some wider button sizes, but for many cases it works for me.
In cases where you can't use a bottom-aligned button label (such as when your button has a horizontally aligned icon), janusz's approach also seems to work. here's a version of janusz's .text reassignment technique in ActionScript rather than MXML:
theButton.addEventListener(FlexEvent.UPDATE_COMPLETE, function (e:FlexEvent):void {
e.target.mx_internal::getTextField().text = e.target.label;
});
The preceding code requires you to import mx_internal and FlexEvent first, as follows:
import mx.events.FlexEvent;
import mx.core.mx_internal;
And here are the results…
Before (note truncation despite ample horizontal space):
After:
The only downside to this approach is you lose the ellipsis, but in my case I considered that a welcome feature.