-
-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Binding.TwoWay setter with parameters given from xaml #557
Comments
Could you format this with line breaks for readability? If I understand correctly, you pass the |
Hey @LyndonGingerich , sorry for the formatation. I did not see I forgot to add the language etc which caused weird formatting. Currently I do not pass my xaml GridControl YET, but I want to pass it. with that twoway TextBox.Text setter. <dx:ThemedWindow
x:Class="ResourcePlanner.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Resource.GUI.Converters;assembly=ResourcePlanner.GUI"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
xmlns:dxwui="http://schemas.devexpress.com/winfx/2008/xaml/windowsui"
xmlns:dxwuin="http://schemas.devexpress.com/winfx/2008/xaml/windowsui/navigation"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:templates="clr-namespace:ResourcePlanner.View.Resources"
Title="{Binding mainWindowTitle}"
Width="auto"
Height="auto">
<dx:ThemedWindow.Resources>
<converters:NavigationServiceAndDataContext x:Key="NavigationServiceAndDataContext" />
</dx:ThemedWindow.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<i:InvokeCommandAction Command="{Binding CloseMainApplication}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="ScrollChanged">
<i:InvokeCommandAction Command="{Binding UpdateUi}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DockPanel>
<Grid>
<dxmvvm:Interaction.Behaviors>
<dxmvvm:KeyToCommand
Command="{Binding ToggleDeveloperMode}"
EventName="PreviewKeyUp"
KeyGesture="Control + Alt + D" />
</dxmvvm:Interaction.Behaviors>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<templates:Ribbon
Grid.Row="0"
FilterProjectsCommand="{Binding ElementName=ViewNavigationFrame, Path=Content.FilterProjectsCommand, UpdateSourceTrigger=PropertyChanged}"
GridControl="{Binding ElementName=ViewNavigationFrame, Path=Content.GridControlProperty, UpdateSourceTrigger=PropertyChanged}"
INavigationFrameService="{Binding ElementName=FrameNavigationService, Path=., UpdateSourceTrigger=PropertyChanged}"
NavigationFrame="{Binding ElementName=ViewNavigationFrame, Path=., UpdateSourceTrigger=PropertyChanged}" />
<templates:ResourcePlannerProjectView
x:Name="ResourcePlannerResourceView"
Grid.Row="1"
Visibility="Collapsed" />
<dxwui:NavigationFrame
x:Name="ViewNavigationFrame"
Grid.Row="1"
AnimationType="None"
NavigationCacheMode="Required">
<dxmvvm:Interaction.Behaviors>
<dxwuin:FrameNavigationService x:Name="FrameNavigationService" />
</dxmvvm:Interaction.Behaviors>
</dxwui:NavigationFrame>
</Grid>
</DockPanel>
</dx:ThemedWindow> In the navigationFrame from DevExpress we can load in usercontrols on runtime and cache them to switch instantly between different usercontrols. GridControl="{Binding ElementName=ViewNavigationFrame, Path=Content.GridControlProperty, UpdateSourceTrigger=PropertyChanged}" In which I have that textbox of which I want to access that gridControl. In short, the gridControl changes dynamically depending on the current usercontrol loaded in by the navigationFrame and I am not able to bind it. Unless you have another idea? Hopefully this all makes sense. Otherwise I will try to make a test project to make it more clear <3 Kind regards, Jelle |
I think I'm starting to understand, but I think my question was unclear. Here's what I meant: Why did you have your |
I think you're going to have to have some code-behind in order to solve this properly. In our project, we have the constraint that our messages have to be fully serializable, which forces all of the logic that you are pushing into the elmish Cmd handler for In some cases, that means we have a WPF control that doesn't expose the right bindings to interop well with Elmish.WPF, so we have to reframe them into a different binding structure using Code-Behind. |
@LyndonGingerich and @marner2 , your two answers made me remember a potential solution for this issue. |
So After the comments you guys left @LyndonGingerich and @marner2 , I managed to actually build an efficient working solution making use of NO code behind actually, but I did make use of DevExpress behaviors. So this solution is only possible in THIS way if you have DevExpress. So as @marner2 suggested there probably would be needed some supporting code because of the way Elmish is setup. Previously our textbox xaml would look like: <TextBox
Width="auto"
MinWidth="25"
HorizontalAlignment="Left"
Text="{Binding AmountOfTimeColumnsToDisplay, Mode=TwoWay, UpdateSourceTrigger=LostFocus}">
</TextBox> And now it looks like the following <TextBox
Width="auto"
MinWidth="25"
HorizontalAlignment="Left">
<dxmvvm:Interaction.Behaviors>
<customClasses:AmountOfColumnsToDisplayGetterHelper AmountOfColumnsToDisplay="{Binding AmountOfTimeColumnsToDisplayGetter, UpdateSourceTrigger=PropertyChanged}" />
<customClasses:AmountOfColumnsToDisplaySetterHelper Command="{Binding AmountOfTimeColumnsToDisplaySetter}" GridControlContext="{Binding Path=GridControl, RelativeSource={RelativeSource AncestorType={x:Type local:Settings}}, UpdateSourceTrigger=PropertyChanged}" />
</dxmvvm:Interaction.Behaviors>
</TextBox> As you can see we removed the binding command AmountOfTimeColumnsToDisplay as substituded it for 2 new bindings so the changed in the init loop: //"AmountOfTimeColumnsToDisplay"
//|> Binding.twoWay ((fun m -> m.AmountOfColumnsToDisplay.ToString()), convertSetAmountOfColumnsToDisplay)
"AmountOfTimeColumnsToDisplayGetter"
|> Binding.oneWay (fun m -> m.AmountOfColumnsToDisplay.ToString())
"AmountOfTimeColumnsToDisplaySetter"
|> Binding.cmdParam convertSetAmountOfColumnsToDisplay the actual code created for these behaviors is the following: /// <summary>
/// This class is used to set the textValue of a textBox when the textBox is fully rendered and visible to the user
/// It will retrieve the value from the DataContext of the TextBox binded to elmish.wpf model
/// "AmountOfTimeColumnsToDisplayGetter" |> Binding.oneWay(fun m -> m.AmountOfColumnsToDisplay.ToString())
/// </summary>
public class AmountOfColumnsToDisplayGetterHelper : Behavior<TextBox>
{
public static readonly DependencyProperty AmountOfColumnsToDisplayProperty =
DependencyProperty.Register("AmountOfColumnsToDisplay", typeof(int), typeof(AmountOfColumnsToDisplayGetterHelper), new PropertyMetadata());
public int AmountOfColumnsToDisplay
{
get { return (int)GetValue(AmountOfColumnsToDisplayProperty); }
set { SetValue(AmountOfColumnsToDisplayProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Loaded += AssociatedObject_Loaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
this.AssociatedObject.Text = AmountOfColumnsToDisplay.ToString();
}
protected override void OnDetaching()
{
this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
base.OnDetaching();
}
}
/// <summary>
/// This class is used to execute a command when the textBox lost focus event is triggered
/// "AmountOfTimeColumnsToDisplaySetter" |> Binding.cmdParam convertSetAmountOfColumnsToDisplay
/// As command parameters it will pass the textValue of the textBox and custom dependency property DevExpress Xpf GridControl
/// These parameters should be given with a multibinding in the xaml file
/// </summary>
///
public class AmountOfColumnsToDisplaySetterHelper : Behavior<TextBox>
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(AmountOfColumnsToDisplaySetterHelper), new PropertyMetadata());
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty GridControlContextProperty =
DependencyProperty.Register("GridControlContext", typeof(GridControl), typeof(AmountOfColumnsToDisplaySetterHelper), new PropertyMetadata());
public GridControl GridControlContext
{
get { return (GridControl)GetValue(GridControlContextProperty); }
set { SetValue(GridControlContextProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.LostFocus += AssociatedObject_LostFocus;
}
private void AssociatedObject_LostFocus(object sender, RoutedEventArgs e)
{
if (Command != null)
{
//create object array with the textValue of the textBox and the gridControl context
object[] commandParameter = new object[2];
commandParameter[0] = this.AssociatedObject.Text;
commandParameter[1] = GridControlContext;
Command.Execute(commandParameter);
}
}
protected override void OnDetaching()
{
this.AssociatedObject.LostFocus -= AssociatedObject_LostFocus;
base.OnDetaching();
}
} And to wrap this all up our cmdParam converter method now works also in the following way: let convertSetAmountOfColumnsToDisplay (p: obj) =
try
//splits p array object into two objects of string and gridcontrol
let args =
let objectArray = p :?> obj array
let stringObject = objectArray.[0] :?> string
let gridControlObject = objectArray.[1] :?> GridControl
(stringObject, gridControlObject)
SetAmountOfColumnsToDisplay args
with
| e ->
Serilog.Log.Logger.Information(e.StackTrace)
UpdateUi Thank you all a lot for the brainstorm and if you guys have any questions regarding me solutions, I gladly explain. Kind regards, Jelle |
So currently I have an interesting usecase. Now that I have separated all my ui and removed it from my model, I need to pass ui objects to the messaged to interact with them. #555
Now I have the following use case for a textbox:
Current Xaml
But we would like to also bind
to it as an extra parameter.
Current binding:
convertSetAmountOfColumnsToDisplay method:
SetAmountOfColumnsToDisplay args command:
Hopefully you guys would have a suggestion on how we would go about and solve this.
This is one of the last steps into making my application pure without ui in the models.
I have tried multibindings, but I got stuck on that part, since a multibinding for the "textbox.text" would not trigger the setter of the elmish loop and would just stay within the converter itself.
The text was updated successfully, but these errors were encountered: