XAML reference control and properties in x:Array - wpf

<RichTextBox x:Name="OrigText" Margin="0,0,8,0" d:LayoutOverrides="Width"/>
<Button x:Name="OrigFileBrowse" Command="{Binding BrowseCommand}" CommandParameter="{Binding ElementName=OrigText, Path=Document}" HorizontalAlignment="Center" Margin="0,0,8,2.442" Width="75" Content="Browse" Grid.Row="1" d:LayoutOverrides="Height"/>
<RichTextBox x:Name="ModifiedText" Grid.Column="1" Margin="8,0,0,0"/>
<Button x:Name="ModifiedFileBrowse" Command="{Binding BrowseCommand}" CommandParameter="{Binding ElementName=ModifiedText, Path=Document}" HorizontalAlignment="Center" Width="75" Content="Browse" Grid.Row="1" Grid.Column="1" Margin="0,0,0,2.442" d:LayoutOverrides="Height"/>
<Button x:Name="Compare" Command="{Binding CompareCommand}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Content="Compare" Grid.Row="2" Grid.ColumnSpan="2">
<Button.CommandParameter>
<x:Array Type="RichTextBox">
<local:CompareTextView/>
</x:Array>
</Button.CommandParameter>
</Button>
Trying to get 2 items to be passed when the Compare button is clicked as it will then execute a compare command. Attempted to make use of MultiBinding however that is firing on instantiation and therefore the converter then fires accordingly. It does NOT fire when I click compare and the compare command is executed.
With that not working, I am attempting to now reference the controls within XAML to pass within an ArrayExtension. Not sure of the syntax or if it is even possible as I know you cannot bind within the ArrayExtension. The above fails since it can not construct a new CompareTextView view, which has no default constructor since I am making use of Prism...
Pretty frustrating, hopefully someone can help me out...
EDIT:
Want to clear some things up. The issue is not that I want CanExecute called again. The issue is that at instantiation of the controls, the converter is called and executed and the values are returned...but where they go I have no clue? The converter is never called again. If I could get the initial references to the FlowDocument this would all be a moot point...but it doesn't return things anywhere per se...since this is a command...if that makes sense...when making use of MultiBinding.
<Button x:Name="Compare" Command="{Binding CompareCommand}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Content="Compare" Grid.Row="2" Grid.ColumnSpan="2">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource FlowDocumentConverter}">
<Binding ElementName="OrigText" Path="Document"/>
<Binding ElementName="ModifiedText" Path="Document"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
UPDATE:
Tried what refereejoe mentions here, scroll down a little bit to see his posting. While CanExecute continually fires, this does nothing to resolve the issue. In addition I switched the MultiBinding to be a single item, it is coming back null. Again when the converter fires on instantiation the FlowDocument references are there...
ANSWER:
Abe's mention that it was being cached led me to try something else. Since I knew that the FlowDocument references were available when the converter was called I knew they were there. Something was getting fouled up. The key piece appears to be in the converter itself. I was simply returning the object[]. Then when the command fired the arg was indeed an object[] but the two items were null. I created a class called Docs, which had two properties, one for each FlowDocument reference. When the converter fired I set the properties appropriately and then returned the Docs object. Now when I initiated the compare command, the Docs object was the args and it had the reference to the FlowDocuments just as I needed! Not sure if this is by design, but the fact that the items get lost when using the object[] doesn't make sense to me.

The proper way to do this is indeed with a MultiBinding on the CommandParameter. You won't see it call your CanExecute method unless WPF is informed that the method could return a different value than it had already cached (via the CanExecuteChanged event).
Since you are relying on the parameter passed in to determine this, we have to raise the event when the parameter changes. Since we can't really determine that in the command, we can use another technique: tell WPF to poll our command anytime it polls UICommands. This is done by implementing your ICommand like so:
public class MyCommand : ICommand
{
public void Execute(object parameter) { /* do stuff */ }
public bool CanExecute(object parameter { /* determine if we can do stuff */ }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
Obviously, this prevents you from using the Prism DelegateCommand, but this will respond to changes in the command parameters.
UPDATE
Another thing to consider is that the Document property on the RichTextBox isn't actually changing. Instead, when you type into it, the content of the FlowDocument changes. Since the property instances don't change, the converter won't get fired again, and the originally converted value will get stored in the CommandParameter property.
One of the ways to force the converter to be called again is to add a Binding to the MultiBinding that is bound to a property that will change every time the text of the RichTextBox changes.
A somewhat hacky solution would be to use the IsKeyboardFocusWithin property, as that will mimic the default binding behavior of TextBox.Text (i.e. when the TextBox loses focus, the Binding updates):
<MultiBinding Converter="{StaticResource FlowDocumentConverter}">
<Binding ElementName="OrigText" Path="Document" />
<Binding ElementName="ModifiedText" Path="Document" />
<Binding ElementName="OrigText" Path="IsKeyboardFocusWithin" />
<Binding ElementName="ModifiedText" Path="IsKeyboardFocusWithin" />
</MultiBinding>
Obviously, in your converter, you will need to ignore these additional values, as they aren't relevant to your conversion.

Related

ICommand overriding SelectedItem

I'm new to WPF. Currently working on a sample app with MVVM pattern. I have a ListView which is populating three columns (Id, Name, Edit).
Here, whenever user clicks on Edit button, he would be redirected to "Edit form" with pre-populated values (Selected item in ListView).
Edit button is bound to ICommnad and im passing SelectedItem as CommandParameter.
My problem is whenever ICommand fires it contains the previously selected item as SelectedItem.
Any idea how to solve this?
You can find source code at: https://github.com/4pawan/WPF_Sample
The problem is that the button command handler is called before the binding engine has had a chance to update the SelectedItem property.
First of all you've got some architectural problems. Putting NavigateToForm in ithe EmpFormViewModel is poor SoC and I suspect you've only done it to make the binding easier to declare. It should really be in the parent EmpViewModel and of type RelayCommand<EmpFormViewModel>:
NavigateToForm = new RelayCommand<EmpFormViewModel>(vm =>
{
this.IsImportDataVisible = false;
this.IsSearchVisible = true;
//do something with vm here
});
Then all you have to do is modify your button command binding to bind to the parent instead and pass in the list item. So replace all of this...
<Button Content="Edit" Command="{Binding NavigateToForm}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource EditEmpConverter }">
<!--<Binding Path="DataContext" ElementName="mainWindow"/>-->
<Binding Path="SelectedItem" ElementName="LstVw"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
...with this:
<Button Content="Edit" Command="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=DataContext.NavigateToForm}" CommandParameter="{Binding}" />
Note that I've also removed the converter from the equation, if you really do need a cloned instance then do it in the ViewModel handler where it can at least be more easily tested. Converters are a powerful tool but they're often abused to compensate for poor binding between view model and views. It's the primary responsibility of the view model to present the data in a form that can be easily consumed by the view, if you find yourself relying on them too much or for very simple tasks (such as the case here) then it's often a good indication that the view model isn't doing its job properly.

Access Datacontext And A Property In Code Behind At The Same Time

I am using MVVM/WPF and trying to do something seemingly simple, but cant find a clean solution.
I want to do the following:
When a property changes in the model (WPF Textbox text would be changed in this case), use a method to perform other operations on the UI relating to the property bound.
Currently i am using a multibinding on the tooltip (to get the textbox datacontext + binding path), but this is a bit of a hack.
<TextBox x:Name="textBox" Text="{Binding Model.MyProperty}">
<TextBox.ToolTip>
<MultiBinding Converter="{StaticResource brNewMultiConverter}">
<!-- This to trigger the converter in all required cases.
Without it, i cant get the event to fire when filling
the model initially
-->
<Binding ElementName="textBox" Path="Text" />
<!-- This has the properties i need, but wont fire without
the binding above -->
<Binding ElementName="textBox" />
</MultiBinding>
</TextBox.ToolTip>
</TextBox>
I would like to make something re-usable and maybe for different controls, hence i am not just using the textchanged event.
If anyone could point me in the right direction, it would be much appreciated.
OK, so far as your Multibinding there, what are you trying to accomplish there? I don't know what your converter is supposed to do, but can it not be done with an IValueConverter implementing class? I am assuming not, it looks like you are passing the textbox to the converter.
As far as having a method do several things when your model properties get updated, you can have the viewmodel subscribe to events on your model class. Just declare the object WithEvents (VB.NET) and add event handlers for On[PropertyName]Changed.
When implementing MVVM, I tend to treat the codebehind as a second class citizen. I do my best to push all logic off to the ViewModel or View if I can. I have almost completely stopped using Converters as much of that logic can be duplicated in ViewModels, and if it is something that I want to re-use, I usually just have a little helper class that gets whatever passed to it, does something, and passes it back out. I have never really had that great a relationship with IValueConverter...
Other than that, it is unclear exactly what you are trying to do. Could we get some more clarification?
It looks like you're trying to have the tooltip have the content of the textbox, if so why not just do this?
<TextBox Text="{Binding Model.MyProperty}" ToolTip="{Binding Model.MyProperty}"/>
If that's not what you want, but want the tooltip to change based on the value of the textbox then do that in your viewmodel e.g.
public class MyViewModel
{
string _MyProperty;
public string MyProperty
{
get { return _MyProperty;}
set
{
_MyProperty = value;
OnPropertyChanged("MyProperty");
OnPropertyChanged("MyToolTipProperty"); //force WPF to get the value of MyToolTipProperty
}
}
public string MyToolTipProperty
{
get
{
//return what you want
}
}
}
and then in your markup:
<TextBox Text="{Binding Model.MyProperty}" ToolTip="{Binding Model.MyToolTipProperty}"/>

Xaml binding for radio/checkbox to toggle between imperial/metric

Pretty much as the title states, I'm grabbing some values from a Db, which are all in Km, but I want to implement a converter which I can toggle between Miles or Kilometers, and want to bind which is displayed to either a checkbox, or a radio button group, whichever is easiest (Radio would be preferred).
I'm thinking I can just use an IValueConverter rather than an IMultiValueConverter, and the Convert/ConvertBack methods, as the default will be Kilometers, but I don't know how to call the ConvertBack method. Or I could pass true/false as the ConverterParameter depending on whether I want Km/Miles displayed.
But either way I'm not sure how to hook up the Xaml Binding on either method (I know how to do a standard value converter binding, but not the extra flumff needed.
Any hints appreciated.
<StackPanel Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
<RadioButton Content="Km" GroupName="rdBtnGrpValue" IsChecked="True" />
<RadioButton Content="Miles" GroupName="rdBtnGrpValue" />
</StackPanel>
And:
<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Top" Grid.Column="1" Text="{Binding EquatorialCircumference, Converter={StaticResource KmMiConv}, StringFormat='{}{0:0,0.0}'}" />
If you are using the MVVM pattern, and are using a view-model as your DataContext, you could use a Mode=TwoWay binding between the RadioButtons and a boolean property in the view-model, something like bool ConvertToImperial { get; set; }.
Your actual conversion can occur in the getter for the EquatorialCircumference property. If ConvertToImperial is true, return the value in miles, otherwise return the value in kilometres.
Then, for the TextBox, you can simply bind it to the EquatorialCircumference property, and the value displayed will be in the selected unit.
You will, however, need to raise a property change notification for any properties whose values are affected by a change in units.

Context Menu for textbox

What exactly I want to do is that there are 2 tables,ie, user and userprofile and both of them have almost identical fields. I shall take example of the email field. There is a textbox and the User table email field value is displayed in it. What I want to do is, have a context menu such that when the user right clicks on the textbox, the menu displays both the User and UserProfile email field values. – developer 1 hour ago
Whatever value one selects from the context menu the textbox then displays that value. You can use Binding Email1 and Binding Email2, as I have no problems getting those two values from database so I shall change my code accordingly. As I am new to WPF and .NET framework itself, I am not sure how to achieve this. Please let me know if I have made myself clear this time.
I am not sure how to handle commands and events. Can anybody show me the code to accomalish this..
<TextBox Style="{StaticResource FieldStyle}" Text="{Binding Email1, UpdateSourceTrigger=PropertyChanged}">
<TextBox.BorderBrush>
<MultiBinding Converter="{StaticResource TextBoxBorderConverter}">
<Binding Path="Email1"/>
<Binding Path="Email2"/>
</MultiBinding>
</TextBox.BorderBrush>
</TextBox>
Thanks in advance
At risk of giving you a WPF/MVVM noob answer and getting flamed, here goes. I can't advise you on databinding with databases since I've never done it, so I will just give you the XAML and it's up to you to work on the database end.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBox Height="28" Text={Binding PreferredEmail}">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Email1}" Command="{Binding Email1Command}" />
<MenuItem Header="{Binding Email2}" Command="{Binding Email2Command}" />
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Grid>
</Page>
In the databinding to objects case, PreferredEmail, Email1, and Email2 would bind to a dependency property or a property that raises the PropertyChanged event. This is how your ViewModel (or whatever you want to call the lower-level code) will update the data. If you change those values in code-behind, ultimately it'll get reflected in the context menu automagically. Then you have to implement two ICommand-based classes to handle the setting of PreferredEmail.
I think it's super lame to implement two command handlers, and it certainly won't scale well if you have to add more email sources. I think a better solution would be to use one command handler and a CommandParameter that is the selected MenuItem header, but I don't know how to do that. But in any case, the two command handler solution will still work if you're in a bind.

Databinding Not Updating When Using {Binding .} or {Binding}

I have an ObservableCollection of addresses that I am binding to a ListBox. Then in the ItemTemplate I am Binding to the current address record using {Binding .}. This results in my addresses displaying using their ToString method which I have setup to format the address. All is good, except if I update properties on an individual address record the list in the UI does not update. Adds/Deletes to the list do update the UI (using the ObservableCollection behavior). If I bind directly to properties on the address the UI does update (using the INotifyPropertyChanged behavior of the Address object).
My question is, is there a way to notify the UI of the change to the object as a whole so that I can still use this syntax or do I need to punt and put a DisplayText property on my address type that calls the ToString method and bind to that? FYI, this is an MVVM architecture so I don't have the luxury of calling Refresh on the ListBox directly.
Thanks for any help/ideas.
<ListBox x:Name="AddressList" ItemsSource="{Binding Addresses}" Background="Transparent" BorderBrush="Transparent"
Width="200" HorizontalAlignment="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding .}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When you bind to the Address object itself, the object itself -- that is, its identity -- doesn't change, even though its properties do. WPF therefore doesn't know to refresh the binding in this case.
So yes, you need to bind to a notifying property (or properties) rather than the whole object. As you say, one way to do this is to create a DisplayText property, and raise the PropertyChanged event for that property whenever something that affects the display text changes. Another is to use multiple TextBlocks in a horizontally oriented StackPanel, each bound to a particular property e.g.
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding HouseNumber}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Street}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding City}" />
</StackPanel>
The advantage of the second approach is that it gives you flexibility in the UI to change how addresses are displayed, e.g. multiple lines, formatting, etc.; the downside is that it gets complicated if you have conditional logic e.g. an optional flat number or second address line.
I tried to reproduce the problem and succeeded.
I activated the step-into-.NET debugging options, and saw that WPF does not listen to INotifyPropertyChanged if the path in the binding is empty.
What worked to get a change to be reflected in the list box is to replace the whole object in the ObservableCollection. This triggers the INotifyCollectionChanged, with the Replace action.
But this may not be acceptable in your case. And it could be seen more like a hack than a solid solution.
I'd seriously consider having a DataTemplate for Address. There you should bind to the exact properties you need (which would create the listener for INotifyPropertyChanged). It is more flexible than ToString() and you may encounter cases where you have a need for ToString() to do something for non-UI stuff, which would create a conflict. And honestly, ToString is not really meant for UI stuff.

Resources