Is it possible to call a helper function in XAML that takes any number of parameters using x:Bind? I have this XAML code:
<Button Content="Button content"
Margin="0,0,10,0"
Command="{Binding SomeCommand}"
IsEnabled="{x:Bind helpers:CommonHelpers.TrueForAll(ViewModel.Property,ViewModel.SomeOtherProperty)}"
VerticalAlignment="Center">
</Button>
where the TrueForAll function looks like this:
public static bool TrueForAll(params object[] objects) => objects.All(x => x != null);
When trying to use this in action with multiple parameters, the compiler says Cannot find a function overload that takes 2 parameters. and when only using one, it expects object[] so it says Expected argument of type 'Object[]', but found 'MyType'
The behavior is related to the params keyword. x:Bind can't recognize params that you defined. It is still looking for method with explicit parameter lists.
My suggestion is that you might need to use List<object> or ObservableCollection<object> as the parameter for the method. And create such a property in your ViewModel as well. This could be accepted by the x:Bind.
Is it possible to call a helper function in XAML that takes any number of parameters using x:Bind?
No.
The solution is to define the method with explicit parameters:
public static bool TrueForAll(IEnumerable<object> objects)
=> objects?.All(x => x != null) == true;
And create the parameter programmatically:
public IEnumerable<object> Input =>
new object[] { ViewModel.Property, ViewModel.SomeOtherProperty };
XAML is a markup language and trying to do things like creating arrays in it is an anti-pattern really. You should create the input parameter in the code-behind of the very same view or move your logic to the view model.
Related
I have a generic method
var propertyResolverMethod = _propertyResolver
.GetType()
.GetMethod(
nameof(_propertyResolver.GetEntities),
new[] { typeof(string), typeof(string) })
?.MakeGenericMethod(_itemType);
and the invocation
var recommendedItems = propertyResolverMethod
?.Invoke(
_propertyResolver,
new object[] { itemId, ResolvedCollectionId });
It on the compile-time it returns an object, but on runtime, it returns IQueryable<Item> this item is the item from _itemType and we find out its type only on runtime, but I know that it has a collection inside Item.RelatedItems that I need to get. Tried already casting to dynamic, but does not work for my case, I know that's somehow solvable via Expressions.
Should be iteratable like this
var itemIds = recommendedItems?.Select(i => i.RelatedItems.Select(s => s.ItemID));
But it's not possible without a proper cast
Hmm..
So if I get you correctly, you know this method returns an IQueryable<Item> where Item is the type previously stored in _itemType and you also know for a fact that this type always defines RelatedItems which in turn are an IEnumerable<T> or IQueryable<T> with Tdefining ItemID?
Is there a type that all possible _itemTypes inherit from that has RelatedItems defined? If so, you could try casting to that...
Else you could do more Reflection magic:
If you know that your result is an IQueryable<TItem>, you can extract the property-getter for RelatedItems with Reflection and pass that to the Select-function (which you can get by eithre casting to IQueryable<object> or by using ... again ... Reflection.
But to be honest that would be a very, very dirty way to do it and I would only do it as a last resort.
My first instinct right now would be to try refactoring the containing class (_itemType hints to me that this is a member field), so it is generic as well (inheriting from a non-generic class if you need that), so the class knows the type through its own type parameter.
Another solution would be to have a function like this:
private <Type of itemIDs> GetRelatedItemIds<TItem>(object source) {
return ((IQueriable<TItem>)source)?.Select(i => i.RelatedItems.Select(s => s.ItemID));
}
Now get that method via Reflection and use MakeGenericMethod(_itemType) on it, then invoke it with your result.
Just beware that your current selection will return a nested enumerable/queryable which each entry being a list of ItemIDs. If you didn't mean to do that, use SelectMany instead of the first Select which will automatically concatenate the inner enumerables/queryables.
I hope this helps you find a solution.
I need to change some functionality in a WPF app we've written. We use MVVM Light to implement MVVM. Whenever we've needed to pass some parameters to a method we've used MVVM Light's Messenger class. I've got to pass 3 parameters to a method, but I thought I'd try doing this without using the Messenger class, but instead I hoped I could do it using the RelayCommand() method. I did a search and found this post here on SO, from some years ago. But at least to me, I think this won't work as it's using just 1 type; string in this case. After making some trials and realizing that I'd done it wrong, I decided I could probably create a class with the 3 values I need in it as properties of the class, put it into Models folder and use
new RelayCommand<MyClass>()
So, first question, just to verify that I've got the right idea, I think I would do something like this:
new RelayCommand<MyClass>((mc) => MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C)
Is that correct?
Assuming the answer to the above is yes, then how do I actually pass parameters to this when I bind to it in the XAML? This command is going to be associated with a button on the window/page, so I'll be using the Button's Command property. How do I actually pass in the values for the MyClass instances Prop_A, Prop_B and Prop_C?
So, first question, just to verify that I've got the right idea, I
think I would do something like this:
new RelayCommand<MyClass>((mc) => MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C)
This is correct.
Assuming the answer to the above is yes, then how do I actually pass
parameters to this when I bind to it in the XAML? This command is
going to be associated with a button on the window/page, so I'll be
using the Button's Command property. How do I actually pass in the
values for the MyClass instances Prop_A, Prop_B and Prop_C?
This will actually depend on where would Prop_A, Prop_B and Prop_C come from. If these properties are already inside your view model, then there is no need for you to pass parameters using XAML.
new RelayCommand<MyClass>((mc) => MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C)
will change to
new RelayCommand<object>((param) =>
{
// param is not used.
var mc = this.MC; // assuming your view model holds the mc value
MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C);
});
We must make sure that when we load our view model, we have everything we need. Else, use an IoC to fetch whatever it is you need to.
Binding a parameter to your command is often useful for something like a calculator app where you want to pass the button value to your command such as 0 - 9.
<Button Grid.Row="0" Grid.Column="1" Content="7" Command="{Binding PerformAction}" CommandParameter="7"/>
I would want to stay away from defining classes in your view. For the separation of concern, the view should only know of the properties to be bounded to and not the models.
So, first question, just to verify that I've got the right idea, I think I would do something like this:
new RelayCommand<MyClass>((mc) => MyMethod(mc.Prop_A, mc.Prop_B, mc.Prop_C)
Is that correct?
Yes, it is.
How do I actually pass in the values for the MyClass instances Prop_A, Prop_B and Prop_C?
Simply create an instance of the class that holds the parameters inside your xaml as command parameter:
<Button Command="{Binding Command}">
<Button.CommandParameter>
<local:MyClass Prop_A="a value" Prop_B="b value" Prop_C="c value" />
</Button.CommandParameter>
</Button>
There is another approach to do this by using IMultiValueConverter:
class MultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (var item in values)
{
//process the properties passed in and you will need to unbox those parameters
}
return new object();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then in xaml (Button code):
<Button Content="Test" Style="{StaticResource contextMenuAware}" Command="{Binding MultiParameterCommand}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource MultiValueConverter}">
<Binding Path="Items"/>
<!-- Pass more Bindings here or static values -->
</MultiBinding>
</Button.CommandParameter>
</Button>
Here is the code for inclusion of the converter:
xmlns:converter="clr-namespace:SO_app.Converters"
And then in you Window Resources tag:
<converter:MultiValueConverter x:Key="MultiValueConverter"/>
This way you can use Binding when passing parameters without implementing DependencyProperties.
Here is how I did it:
Your CommandRelay object takes object as parameter, you convert that object to object[] then each element to its own object.
private RelayCommand<object> _YourCommand;
public RelayCommand<object> YourCommand
{
get
{
return _YourCommand ?? (_YourCommand = new RelayCommand<object>(p =>
{
var values = (object[]) p;
int item1 = int.Parse(values[0].ToString());
string item2 = values[1].ToString();
double item3 = double.Parse(values[2].ToString());
}));
}
}
Then, in xaml (Of course, your Paths in Binding must be valid references to your binded objects)
<Button Command="{Binding YourCommand}">
<Button.CommandParameter>
<MultiBinding>
<Binding Path="Item1"/>
<Binding Path="Item2"/>
<Binding Path="Item3"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
I'm some what new to Generics and I can't figure out why the following doesn't work.
I have an extension to IEnumerable<T>, called Grid and it looks like so
public static class IEnumberableGridExtension
{
public static HelperResult Grid<T>(this IEnumerable<T> gridItems, Action<GridView<T>> thegrid)
{
........
}
}
Say, I have a variable in my razor Model called "Products" and it is the type List<Product>, so I tried to do
#Model.Products.Grid<Product>(grid=>{
...
});
It states "Cannot convert method group 'Grid" to non-delegate type 'object'. Did you intend to invoke the method?", with "#Model.tasks.Grid" red underlined.
The funny thing is, visual studio compiles, everything is fine. Of course, if i simply do
#Model.Products.Grid(grid=>{
...
});
It's all fine.
The Razor parser is interpreting < and > as normal HTML tags.
You can avoid the problem wrapping the call in parenthesis:
#(Model.Products.Grid<Product>(grid=>{
...
}))
You can find additional info here: How to use generic syntax inside a Razor view file?
I'm trying to create a method signature that takes multiple properties of various type using
I would call it something like this:
AllPropertiesExcept(() => Property1, () => Property2)
This method almost work, except that the type of the properties have to be the same. I'm only going to use the property name, but want to use lambda expression to enable easy refactoring.
public static string MyMethod<T>(params Expression<Func<T>>[] propertyExpression)
I would use AllPropertiesExcept(params Expression<Func<object>>[] properties), you can still get the property names out of it, but it doesn't matter what type the property is.
Edit: However, I would tend to use it the other way round - instead of excluding properties I don't want to see, I would include properties I want to see. The reason is simple - to make your way work, you still need reflection - with my way, you could easily use the Func you get to get the actual data directly.
Edit 2 (getting the property name out of an expression):
Expression<Func<object>> obj = something; // you get this in your method
((obj.Body as UnaryExpression).Operand as MemberExpression).Member.Name
I can really advise you to use LinqPad for such things, you can easily drill down objects via Dump(), which displays the objects very user friendly. Just recreate a small example and experiment.
Does the method AllPropertiesExcept() return anything? Otherwise you could make a fluent interface (using method chaining):
AllPropertiesExcept(() => Property1)
.And(() => Property2)
.And(() => Property3);
Even if the AllPropertiesExcept() method returns something, you can defer the execution until you invoke a method at the end of the method chain:
var foo = AllPropertiesExcept(() => Property1)
.And(() => Property2)
.And(() => Property3)
.DoSomeThing();
I think what you need is to understand the ModelMetadata class documented here:
http://msdn.microsoft.com/en-us/library/system.web.mvc.modelmetadata.aspx
This class is used in ASP.NET MVC in situations like Html.LabelFor(x -> x.Name)
An expression is passed to the ModelMetadata.FromLambdaExpression Method documented here:
http://msdn.microsoft.com/en-us/library/ee428393.aspx
After understanding how it is used in MVC, you could create your own code with some informed knowledge of how it was applied elsewhere.
I have a system.collections.generic.list(of ListBox)
I would like to use the collection classes built-in Find method to find a particular ListBox by the Name of the Listbox
I found the following MSDN article
http://msdn.microsoft.com/en-us/library/x0b5b5bc.aspx
This does not work for me because it does not show how to find a SPECIFIC instance in the collection by the collection name.
The example hard codes the search String of "saurus" into the Find predicate. I want to pass in the search name. When I add a variable I the FindListBox function I created I get an error saying the signature doesn't match.
I know I can add a private method to my own class but now I am curious how to implement the Find in the Generic so I can do this.
Seth
Assuming you're using C# 3:
string nameToFind = "saurus";
ListBox found = list.Find(x => x.Name == nameToFind);
For C# 2:
string nameToFind = "saurus";
ListBox found = list.Find(delegate (ListBox x) { return x.Name == nameToFind; });
(Yes, this is still hard-coding the value, just for sample purposes. But the nameToFind variable could be a method parameter, or the result of a method call etc.)
The example on MSDN could be extended as follows:
private static ListBox EndsWith(String s, List<ListBox> list)
{
return list.Find(
delegate(ListBox l)
{
return l.Name.EndsWith(s);
}
);
}
This uses an anonymous delegate to perform the search. The reason why you're getting signature errors is that this delegate, which is the same as the EndsWithSaurus method used in the example, must take a single parameter of the type being searched. I haven't tested this, but this is where I'd go. You can further extend on this principle by making this a templated method that takes a List of T and returns T.
Alternatively, you can use lambda expressions in C# 3.0:
list.FirstOrDefault(x => x.Name.EndsWith("something");