Quantcast
Channel: Caliburn.Micro: Xaml Made Easy
Viewing all 1760 articles
Browse latest View live

Commented Unassigned: Action guard method re-evaluation [312]

$
0
0
I wanted to get opinions from the dev's on items like below see https://caliburnmicro.codeplex.com/discussions/442668 for original source.

On the one hand something like this could be integrated into Caliburn.Micro proper, on the other hand it could make more sense for the original author to package it up as an recipe.

Thoughts?


Every now and then, there is a new user that asks how to trigger availability update for action guards implemented as methods. I am aware that such methods are re-evaluated every time a parameter changes, but there are some cases where the evaluation of a guard depends on both the parameters and the internal state of the class providing the action. In such a scenario, it can be useful to have a way to forcefully request an availability update on the action.

I decided to provide a possible solution, involving a simple naming convention and the use of specific events: consider and action called 'Execute(...)' and the associated method guard 'CanExecute(...)'; my idea is to modify the PrepareContext implementation to check for the existence of a specific event, called 'ReEvaluateCanExecute' and, if available, attach to it and invoke UpdateAvailability whenever the event is invoked.
Since PrepareContext is an extensibility point, this feature can be easily added, without modifying the current CM code base.

The actual implementation is provided below:
``` C#
namespace ActionGuardSample
{
#region Namespaces
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Caliburn.Micro;
using Action = System.Action;

#endregion

/// <summary>
/// Static class used to provide Caliburn Micro extensions.
/// </summary>
public static class CaliburnMicroExtensions
{
#region Static Methods
/// <summary>
/// Prepares the context.
/// </summary>
/// <param name="context">The context.</param>
private static void PrepareContext(ActionExecutionContext context)
{
ActionMessage.SetMethodBinding(context);
if (context.Target != null && context.Method != null)
{
var targetType = context.Target.GetType();
var guardName = string.Format("Can{0}", context.Method.Name);
var guard = TryFindGuardMethod(context);
if (guard == null)
{
var inpc = context.Target as INotifyPropertyChanged;
if (inpc == null)
return;
guard = targetType.GetMethod(string.Format("get_{0}", guardName));
if (guard == null)
return;
var handler = (PropertyChangedEventHandler)null;
handler = ((s, e) =>
{
if (string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == guardName)
{
((Action)(() =>
{
var message = context.Message;
if (message == null)
inpc.PropertyChanged -= handler;
else
message.UpdateAvailability();
})).OnUIThread();
}
});
inpc.PropertyChanged += handler;
context.Disposing += (s, e) => inpc.PropertyChanged -= handler;
context.Message.Detaching += (s, e) => inpc.PropertyChanged -= handler;
context.CanExecute = () => (bool)guard.Invoke(context.Target, MessageBinder.DetermineParameters(context, guard.GetParameters()));
}
else
{
var updateEventName = string.Format("ReEvaluate{0}", guardName);
var updateEvent = targetType.GetEvent(updateEventName);
if (updateEvent != null)
{
var target = context.Target;
EventHandler handler = null;
handler = (s, e) => ((Action)(() =>
{
var message = context.Message;
if (message == null)
updateEvent.RemoveEventHandler(target, handler);
else
message.UpdateAvailability();
})).OnUIThread();
updateEvent.AddEventHandler(target, handler);
context.Disposing += (s, e) => updateEvent.RemoveEventHandler(target, handler);
context.Message.Detaching += (s, e) => updateEvent.RemoveEventHandler(target, handler);
}

context.CanExecute = () => (bool)guard.Invoke(context.Target, MessageBinder.DetermineParameters(context, guard.GetParameters()));
}
}
}

/// <summary>
/// Tries to find the guard method.
/// </summary>
/// <param name="context">The context.</param>
/// <returns>The guard method.</returns>
private static MethodInfo TryFindGuardMethod(ActionExecutionContext context)
{
var name = string.Format("Can{0}", context.Method.Name);
var method = context.Target.GetType().GetMethod(name);
if (method == null)
return null;
if (method.ContainsGenericParameters)
return null;
if (typeof(bool) != method.ReturnType)
return null;
var methodParameters = method.GetParameters();
var contextMethodParameters = context.Method.GetParameters();
if (methodParameters.Length == 0)
return method;
if (methodParameters.Length != contextMethodParameters.Length)
return null;
return methodParameters.Zip(contextMethodParameters, (x, y) => x.ParameterType == y.ParameterType).Any(x => !x) ? null : method;
}

/// <summary>
/// Enables support for action guard methods re-evaluation, through a specific event naming convention.
/// </summary>
public static void EnableActionGuardMethodReEvaluateSupport()
{
ActionMessage.PrepareContext = PrepareContext;
}
#endregion
}
}
```
You can download a working sample [here](http://www.mediafire.com/?irqiiq9bvbn41x4).
Comments: Is it completely wrong to implement this notification via the usual INotifyPropertyChanged? BladeWise's implementation above use same call to __ActionMessage.UpdateAvailability()__ method. I mean, new interface, like tibel suggested, would keep this clean, but would require more plumbing code to get this work (and what about Screen/Conductors? They would need to be altered to implement this interface, otherwise there's the plumbing code issue). Also, hard-coded strings should no longer be used - I think people should use either __NotifyOfPropertyChange<TProperty>__ or assembly weaving via Fody (or other library) and if that doesn't cover all scenarios of property notification, then they should use __NotifyOfPropertyChange<TProperty>__. So for guard methods a nice extension method for __INotifyPropertyChanged__ interface could be created (or for __PropertyChangedBase__ if we don't want to pollute every INPC object with this method). And this way, there's no need for additional notification event (when __INotifyPropertyChanged.PropertyChanged__ event is readily available). Method overloads are another beast, though - but as far as I remember from browsing through CM's code, they don't have much support apart from parameter count (supporting only this in guard method re-evaluation should be relatively simple). Small note: > raising a property changed for a parameter is a possible workaround, but feels quite dirty Works, but only for VM properties. I have __BindableCollection<T>__ in VM and I consume it via ItemsControl in view - as you can see this workaround won't work in such case (or maybe it could if collection item was INPC object, I'm not sure - but in my case it isn't).

New Post: Problem with view-model-first approach

$
0
0
Wow. That just seems very wrong. The View has knowledge of the ViewModel. This seems to break the whole reasoning for MVVM.

New Post: Multiples views - viewModel mapping, with custom naming conventions

$
0
0
Hi,
Here's my problem:
I have a ViewModel which should be displayed in two locations at the same time, using two differents views (a mainView and a ToolBarView).
So I had 3 files:
  • SomeWorkspace.MyViewModel
  • SomeWorkspace.MyView
  • SomeWorkspace.My.ToolBarView
<ContentControl cal:View.Model="{Binding MyViewModel" cal:View.Context="ToolBarView"/>
Everything worked fine, ToolBarView and MyView were displayed correctly.

Yesterday, I needed to support a new ViewModel naming: MyViewModel => MyDocument, but MyViewModel should still be supported.
So I added a rule in the BootStrapper.Configure():
ViewLocator.NameTransformer.AddRule("Document$", "View");
Now the ToolBar isn't displayed anymore (but MyView is).
The arborescence is:
  • SomeWorkspace.MyDocument
  • SomeWorkspace.MyView
  • SomeWorkspace.My.ToolBarView
I'm trying to figure out what happens, but in debug mode I already saw that the documentRule is messing up the ToolBar location.

Can someone helps me ? Maybe the way I added the rule was bad, I'm still on it at the moment.

You can easily reproduce the problem, but I can upload a sample project somewhere if you need.

Thanks,

Ph

Updated Wiki: Design-Time Support

$
0
0

Design-Time Support

Enabling Caliburn.Micro inside the Visual Studio designer (or Blend) is quite easy.

You have to set a Desinger-DataContext and tell CM to enable its magic in your view XAML:

<Windowxmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:vm="clr-namespace:CaliburnDesignTimeData.ViewModels"xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro.Platform"mc:Ignorable="d"d:DataContext="{d:DesignInstance Type=vm:MainPageViewModel, IsDesignTimeCreatable=True}"cal:Bind.AtDesignTime="True">

For this to work, the ViewModel must have a default constructor. If this isn't suitable, you can also use a ViewModelLocator for your design-time ViewModel creation.

Issues

It seems that VS2010 has an issue in the WP7 designer and an exception in CM ConventionManager is thrown. You can workaround this by overriding ApplyValidation in your bootstrapper:
ConventionManager.ApplyValidation = (binding, viewModelType, property) => {
        if (typeof(INotifyDataErrorInfo).IsAssignableFrom(viewModelType)) {
            binding.ValidatesOnNotifyDataErrors = true;
            binding.ValidatesOnExceptions = true;
        }
    };

Source code checked in, #c239dee4fa7b74bfe52476e47f9a7bc69e7dc14d

$
0
0
workaround issue EntryPointNotFoundException in Design Mode

New Post: EntryPointNotFoundException in Design Mode

$
0
0
Seems to be an issue with the WPF designer with INotifyCollectionChanged and Portable Class Library.
Implemented a workaround in latest commit that prevents the exception in the designer.

New Post: EntryPointNotFoundException in Design Mode

New Post: Problem with view-model-first approach

$
0
0
@MBonafe: No, it isn't. The view needs to be aware of some concepts of the view-model, to be able to interact with it, whatever flavour of MVVM you decide to use. The View.Model property is indeed part of a view-model-first approach, since the content of the view is dynamically generated from a view-model.

The difference between view-first and view-model-fist, is about which element is created first. In view-first you generate views, and then derive (or generate) the view-model to be attached. In view-model-first, view-models are generated, and views are derived with some mechanism. In either case, the view needs to know which properties or actions are available. Without this knowledge, no MVVM is possible (there is a relationship you need to be aware of, to allow interaction between views and view-models).

New Post: Namespace error in Expression Blend

$
0
0
I am using VS2013, version 1.5.2.0 of Caliburn.Micro and I am having this problem, too. The markup works on execution, but the page does not render in Blend or during editing in VS2013. What can be done do fix this?

Created Unassigned: ActionMessage Not in Namespace [358]

$
0
0
I am using VS2013 to create a WPF 45 project. Everything works fine except for the ActionMessage. I have tried setting up the namespace two ways:

1. xmlns:cal="http://www.caliburnproject.org"
2. xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"

Then, setting up a button to use an Action using either of 2 methods:

Method 1:
```
<Button Content="{Binding ConfirmButtonText}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ConfirmCommand"></cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
```
Method 2:
```
<Button Content="{Binding ConfirmButtonText}"
cal:Message.Attach="[Event Click] = [Action ConfirmCommand()]" />
```

In all cases, the code runs as designed. However the page will not display properly in Blend or the VS designer because of an "Invalid Markup" error:

* The name "Message" does not exist in the namespace (whichever is referenced by cal:).
* The name "ActionMessage" does not exist in the namespace (whichever is referenced by cal:)

I want to use this product, but this is a showstopper. If the forms are not compatible with the designer or Blend, then I must abandon this otherwise awesome library.

Can anyone help?

New Post: ActionMessage Not in Namespace

$
0
0
I am using VS2013 to create a WPF 45 project. Everything works fine except for the ActionMessage. I have tried setting up the namespace two ways:
  1. xmlns:cal="http://www.caliburnproject.org"
  2. xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
Then, setting up a button to use an Action using either of 2 methods:

Method 1:
<Button Content="{Binding ConfirmButtonText}">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <cal:ActionMessage MethodName="ConfirmCommand"></cal:ActionMessage>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
Method 2:
<Button Content="{Binding ConfirmButtonText}" 
                cal:Message.Attach="[Event Click] = [Action ConfirmCommand()]" />
In all cases, the code runs as designed. However the page will not display properly in Blend or the VS designer because of an "Invalid Markup" error:
  • The name "Message" does not exist in the namespace (whichever is referenced by cal:).
  • The name "ActionMessage" does not exist in the namespace (whichever is referenced by cal:)
I want to use this product, but this is a showstopper. If the forms are not compatible with the designer or Blend, then I must abandon this otherwise awesome library.

I realize that I can just name the button and have it run the command. This works and the page is displayed properly in Blend and the VS editor. The real issue, though, is that I cannot use ActionMessage without creating problems in the editors - and I know for sure I will be using them.

Can anyone help?

New Post: Should DeactivateItem on a Collection.OneActive change ActiveItem?

$
0
0
In part of my program I have a Collection.OneActive, and I have a few screens in the collection that I can bring up with buttons that call ActivateItem. If I call DeactivateItem(ActiveItem), the item's OnDeactivate is called, but ActiveItem is not changed (and so it is still displayed in the view).

Looking at the code it seems like DeactivateItem's only effect is to set the items IsActive flag to false, so you end up with ActiveItem.IsActive == false (but the conductor has conductor.IsActive == true, and the view is unchanged).

I'm not sure if this is the intended behavior and my understanding is incorrect, but I expected that deactivating the current ActiveItem would clear or switch ActiveItem. In other words, I expected it to behave like: conductor.ActiveItem = null or conductor.ActivateItem(null), which deactivates and switches the current ActiveItem (thus removing it from the view).

Commented Unassigned: ActionMessage Not in Namespace [358]

$
0
0
I am using VS2013 to create a WPF 45 project. Everything works fine except for the ActionMessage. I have tried setting up the namespace two ways:

1. xmlns:cal="http://www.caliburnproject.org"
2. xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"

Then, setting up a button to use an Action using either of 2 methods:

Method 1:
```
<Button Content="{Binding ConfirmButtonText}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ConfirmCommand"></cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
```
Method 2:
```
<Button Content="{Binding ConfirmButtonText}"
cal:Message.Attach="[Event Click] = [Action ConfirmCommand()]" />
```

In all cases, the code runs as designed. However the page will not display properly in Blend or the VS designer because of an "Invalid Markup" error:

* The name "Message" does not exist in the namespace (whichever is referenced by cal:).
* The name "ActionMessage" does not exist in the namespace (whichever is referenced by cal:)

I want to use this product, but this is a showstopper. If the forms are not compatible with the designer or Blend, then I must abandon this otherwise awesome library.

Can anyone help?
Comments: I can't reproduce your issue with CM 1.5.2 (see attached sample project).

Closed Unassigned: ActionMessage Not in Namespace [358]

$
0
0
I am using VS2013 to create a WPF 45 project. Everything works fine except for the ActionMessage. I have tried setting up the namespace two ways:

1. xmlns:cal="http://www.caliburnproject.org"
2. xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"

Then, setting up a button to use an Action using either of 2 methods:

Method 1:
```
<Button Content="{Binding ConfirmButtonText}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ConfirmCommand"></cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
```
Method 2:
```
<Button Content="{Binding ConfirmButtonText}"
cal:Message.Attach="[Event Click] = [Action ConfirmCommand()]" />
```

In all cases, the code runs as designed. However the page will not display properly in Blend or the VS designer because of an "Invalid Markup" error:

* The name "Message" does not exist in the namespace (whichever is referenced by cal:).
* The name "ActionMessage" does not exist in the namespace (whichever is referenced by cal:)

I want to use this product, but this is a showstopper. If the forms are not compatible with the designer or Blend, then I must abandon this otherwise awesome library.

Can anyone help?

New Post: ActionMessage Not in Namespace

$
0
0
Which Caliburn.Micro are you using? 2.0 or 1.52?

New Post: ActionMessage Not in Namespace

$
0
0
Thanks for the reply!

I'm using 1.5.2.

I have searched for a way to download version 2. It's in an Alpha stage and downloads are not offered. What do you know that I do not?

New Post: Validation on ComboBox Issue

$
0
0
Setup:
VS2013
WPF45
Caliburn.Micro v1.5.2

Setting up a combobox in xaml using the name "StateCombo":
<ComboBox x:Name="StateCombo" Grid.Row="2" Grid.Column="1" Margin="5" />
With the property, "SelectedStateCombo" in the ViewModel:
        public USStateViewModel SelectedStateCombo
        {
            get { return _selectedStateCombo; }
            set 
            {
                SetProperty(ref _selectedStateCombo, value);
                NotifyOfPropertyChange(() => Progress);
            }
        }
I am using a ViewModel as a template for each item in the combobox. This works beautifully! The problem I'm having is getting the ErrorTemplate to display on the combobox. I find that I have to add markup to the xaml to get it to work properly.
<ComboBox x:Name="StateCombo" Grid.Row="2" Grid.Column="1" Margin="5"
                  SelectedValue="{Binding SelectedStateCombo, 
                    ValidatesOnDataErrors=True, 
                    UpdateSourceTrigger=PropertyChanged}" />
I haven't checked yet, but I think the ValidatesOnDataErrors setting is skipped in Caliburn.Micro. Is there anything I can do to make this work without the extra xaml markup?

New Post: ActionMessage Not in Namespace

$
0
0
you can get V2 via Source tab and download a zip file...

well for starters neither of those methods are necessary but if you choose to go that route that is fine with the use of the or Interactivity approach or Message.Attach but as to the reason why it's not working either situation is a mystery.. Both options should work just fine. Try the option below and see if it throws a fit...

option 3
<Button x:Name="ComfirmCommand" Content="Comfirm?" />
anyone can download the alpha source just have to go to the right location to get it

Image

New Post: Validation on ComboBox Issue

New Post: ActionMessage Not in Namespace

$
0
0
NuGet is the preferred way to download/use Caliburn.Micro.
Also 2.0-alpha is available there (you have to select show PreReleases).
Viewing all 1760 articles
Browse latest View live