I'm trying to understand why calls to BindingExpression.ValidateWithoutUpdate() doesn't actually do what it advertises.
I've got some cranky validation (I've removed the boring details from my sample code; suffice it to say it has to do with WF4 ModelItem limitations) that I have to add to a binding via an attached property (e.g., from code and not in xaml).
// d is DependencyObject and prop is DependencyProperty
var binding = BindingOperations.GetBinding(d, prop);
binding.ValidationRules.Add(new MyDerpyValidatonRule());
Nothing crazy here. But the problem is that validation isn't run the first time the control is shown, so validation errors are not exhibited in the UI.
<TextBox
Grid.Column="1"
x:Name="derp"
Text="{Binding Derp, NotifyOnValidationError=True,
ValidatesOnDataErrors=True}"
t:MyDerpyValidator.TargetProperty="{x:Static TextBox.TextProperty}" />
Binding looks good, works after the value is changed, but when first shown, I get a frownyface instead of the expected red Border:
Initially, I tried calling ValidateWithoutUpdate after I added the ValidationRule to the Binding. This didn't seem to work. Later, I used the Dispatcher to try and put this call off until the application was all warm and cozy (maybe it didn't validate because the tea hadn't finished brewing yet, hell I dunno)
var exp = BindingOperations.GetBindingExpression(d, prop);
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action<BindingExpression>)(x =>
{
x.ValidateWithoutUpdate();
}),
DispatcherPriority.ApplicationIdle,
exp);
I'm pretty sure this worked once. Once. Never worked again. Might have been an incorrect observation on my part.
Later, I tried all kinds of things to get ValidateWithoutUpdate to actually do something. I even tried calling it from within an event handler that would happen way down the road
public DerpyControl()
{
InitializeComponent();
derp.MouseEnter += DERPDAMNYOU;
}
void DERPDAMNYOU(object sender, MouseEventArgs e)
{
derp.GetBindingExpression(TextBox.TextProperty).ValidateWithoutUpdate();
}
It never friggen works.
What do I need to do to get BindingExpression.ValidateWithoutUpdate() to friggen validate?!?!
Wow, that's some weird behaviour. Looking at some of the validation code with ILSpy it has some checks in the background that determine if validation is required or not, and I was too scared to follow it all the way through, so, I too, just tried some random stuff.
This is the first thing I tried that worked:
In your attached property changed handler, after
binding.ValidationRules.Add(new MyDerpyValidatonRule());
add
BindingOperations.ClearBinding(d, prop);
BindingOperations.SetBinding(d, prop, binding);
This must somehow set the internal 'validation requred' flag which forces it to validate.
The solution for my project was:
var binding = BindingOperations.GetBindingExpression(textbox, TextBox.TextProperty);
binding.UpdateSource();
binding.ValidateWithoutUpdate();
Related
I have a pixelsense tabletop and want to recognize if a tag was added. Since the hardware is quite old, it is hard to find information. Here I could find an example how to recognize tags, but only with predefined values:
<s:TagVisualizer Name="TagVisualizer">
<s:TagVisualizer.Definitions>
<s:ByteTagVisualizationDefinition Value="192"
Source="SampleTagVisualization.xaml"
UsesTagOrientation="True"
TagRemovedBehavior="Fade"
PhysicalCenterOffsetFromTag="7.5,4.5"/>
</s:TagVisualizer.Definitions>
</s:TagVisualizer>
I just want to raise an event, if a tag (whatever value) was added and then match with a database entry. This should work with at least two objects at the same time.
In the TagVisualizer class I found the following events:
public event TagVisualizerEventHandler PreviewVisualizationInitialized;
public event TagVisualizerEventHandler VisualizationAdded;
public event TagVisualizerEventHandler PreviewVisualizationMoved;
public event TagVisualizerEventHandler PreviewVisualizationRemoved;
public event TagVisualizerEventHandler VisualizationInitialized;
public event TagVisualizerEventHandler PreviewVisualizationAdded;
public event TagVisualizerEventHandler VisualizationMoved;
public event TagVisualizerEventHandler VisualizationRemoved;
But as as far as I've tried, the VisualizationAdded event is only raised, if there exist some TagVisualizer Definitions.
You can use the ContactDown event on any element to look at any surface input. IIRC, there is a Contact.ContactType property on that event which should tell you it’s a tag and then Contact.TagValue tells you the value. This even and it’s peers behave the same as MouseDown and it’s peers.
For TagVisualizer, the purpose of this is to simplify a lot of common code needed to work with tags beyond detecting them. Generally speaking, you’ll want to have a database that defines the tag values you care about along with the positioning of those tags relative to the physical object. A good practice would be to query your database for all of this info and then configure TagVisualizer to be on the lookout for all of those tags you care about. There won’t be a noticeable performance impact to having a lot of definitions configured.
Btw, for API docs, you should see a .chm file inside the SDK installation which will let you read the docs offline since they are no longer accessible online.
This is all from memory so I hope it’s somewhat accurate and helpful!
(disclaimer: former PM for Surface dev platform)
I tried the suggestion by Robert Levy and caught the TouchDown Event on the main panel <Grid TouchDown="TouchDetected"> and printed out the resulting tag value
private void TouchDetected(object sender, TouchEventArgs e)
{
if (e.TouchDevice.GetIsTagRecognized())
{
var tagData = e.TouchDevice.GetTagData();
Console.WriteLine("Touch_VALUE: " + tagData.Value);
}
else
{
Console.WriteLine("No Tag recognized.");
var tagData = e.TouchDevice.GetTagData();
Console.WriteLine("No_Touch_VALUE: " + tagData.Value);
}
}
As it always returned 0, I tried a different approach and added a TagVisualizer to the main Window with a single TagVisualizationDefinition (other way the event VisualizationAdded is not invoked)
<s:TagVisualizer x:Name="MyTagVisualizer"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="Transparent"
Width="auto"
Height="auto">
<s:TagVisualizer.Definitions>
<s:TagVisualizationDefinition
Source="UserControls/ProductMenuVisualization.xaml"/>
</s:TagVisualizer.Definitions>
</s:TagVisualizer>
Even with one Defintion I the event is fired for every tag and I can add the values to a Dictionary and check if it is already added or not.
If you wonder, I subscribe to the events in code
MyTagVisualizer.VisualizationAdded += AddProductMenu;
MyTagVisualizer.VisualizationRemoved += RemoveProductMenu;
MyTagVisualizer.VisualizationMoved += MoveProductMenu;
It is not perfekt but it works.
I'm having difficulty adding the inline of specific type InlineUIContainer into the InlineCollection (Content property) of a TextBlock. It appears the .Add() method of InlineCollection doesn't accept this type, however you can clearly set it through XAML without explicitly marking the content as a InlineContainer, as demonstrated in many examples:
http://msdn.microsoft.com/en-us/library/system.windows.documents.inlineuicontainer.aspx
Is it possible to programatically add one of these as in the following?
Target.Inlines.Add(new Run() { Text = "Test" });
Target.Inlines.Add(new InlineUIContainer() {
Child = new Image() { Source = new BitmapImage(new Uri("http://example.com/someimage.jpg")) } });
Target.Inlines.Add(new Run() { Text = "TestEnd" });
I have a feeling what's going on is that Silverlight is using a value converter to create the runs when specified in XAML as in the example which doesn't use InlineContainer, but I'm not sure where to look to find out.
The specific error I'm getting is as follows:
Cannot add value of type 'System.Windows.Documents.InlineUIContainer' to a 'InlineCollection' in a 'System.Windows.Controls.TextBlock'.
As pointed out by Jedidja, we need to use RichTextBox to do this in Silverlight.
You can't Add() Runs directly, but you can add Spans containing Runs.
Interestingly, you can also do this:
textBlock.Inlines.Clear();
textBlock.Inlines.Add(new Span());
textBlock.Inlines[0] = new Run();
Not that it's a good idea to hack around what the framework is actively trying to prevent you from doing.
P.S. If you can't figure out what XAML is doing, inspect the visual tree.
Imagine the following:
class Repository
{
private ObservableCollection<ModelClass> _allEntries;
public ObservableCollection<ModelClass> AllEntries
{
get { return _allEntries; }
set { _allEntries = value; }
}
public void RefreshDataFromDB()
{
_all = new ObservableCollection(GetMyData()); // whatever method there is
}
}
Now there are a couple of controls that bind to this collection, e.g.:
<ListView ItemsSource="{Binding Repository.AllEntries, ElementName=Whatever}"/>
The problem now is that if I call the RefreshDataFromDB the bindings get lost (at least it seems so) as the _all is now pointing to new memory part and the bindings still use the old reference. INotifyPropertyChanged does not help me in this case (e.g. putting it in RefreshDataFromDB does not help a lot).
The question would be - how would you handle a case where you replce a collection and want to update its consumers' bindings?
Yes; you're not modifying the collection, the UI is bound to the collection, and then you replace it with a new one.
You could do this:
_all.Clear();
_all.AddRange(GetMyData());
Hope that helps!
Alternatively, make AllEntries (or All.. your nomenclature seems to change a few times on the post ;)) a DependencyProperty:
public static DependencyProperty AllEntriesProperty = DependencyProperty.Register("AllEntries", typeof(ObservableCollection), typeof(MyClass));
You'd need to make the get/set property too, see here for an example:
http://msdn.microsoft.com/en-us/library/ms752914.aspx
We're trying to use an ICommand to set up a button in Silverlight with Prism. We'd like the button to be disabled on occasion. DelegateCommand takes 2 parameters, an "ExecuteMethod" and a "CanExecuteMethod"
When we set up the ICommand we expect that if the "CanExecuteMethod" is used, then it will be called to see if the "ExecuteMethod" can be called. The button's Enabled state should reflect the result of the "CanExecuteMethod"
What we actually see:
When the form is created, the method is called and the button is enabled or disabled. (in this case, Enabled)
The CanExecuteMethod is never called again and the Execute method will fire even though we've tried to set behavior to keep that from happening. Execption is thrown (what we'd like to avoid).
The obvious answer is that we should be calling some sort of :
OnPropertyChanged("SaveCommand");
but we're doing it wrong somehow. Either we're assuming that it works a way that it doesn't, or we're missing a step. Any Ideas?
Code:
SaveCommand = new DelegateCommand<string>(OnSaveCommand, CanSave);
public void OnSaveCommand( string helpNumber )
{
OnPropertyChanged("SaveCommand");
//DoSaveStuff
}
public bool CanSave(Object sender)
{
return Model.CanSave();// true or false depending
}
Your SaveCommand, because it is a DelegateCommand, has a function called RaiseCanExecuteChanged().
When you call this function it will have the control refresh from the CanSave function.
The OnPropertyChanged equal for DelegateCommands is MyCommand.RaiseCanExecuteChanged.
Have fun!
I've got two combo's 'Make' and 'Model', they've got their SelectedValue properties bound to an Vehicle object with a ModelID and a MakeID.
Heres Model ...
<ComboBox DisplayMemberPath="Description" ItemsSource="{Binding Path=ModelSpecs}" SelectedValue="{Binding Path=Vehicle.ModelID}" SelectedValuePath="ID" />
A user can search for Vehicles in a seperate control and this swaps out the underlying Vehicle object. Everything works fine if your switching between vehicles of the same Make, however if the Make changes I go away to the database and reload the ModelSpec collection. The combo dosnt display the Model Description because the binding needs to be refreshed.
My current work-around is to add this at the end of the method thats reloading the Models - it works fine, but is not a particularly elegent solution.
var modelID = ViewModel.Vehicle.ModelID;
ViewModel.Vehicle.ModelID = string.Empty;
ViewModel.Vehicle.ModelID = modelID;
Basically I'm just triggering the INotifyPropertyChanged ...
private string _modelID;
public string ModelID
{
get { return _modelID; }
set
{
if (_modelID == value) return;
_modelID = value;
OnPropertyChanged("ModelID");
}
}
I can think of a couple of similar inelegant solutions - but there must be a better way?! Any help appreciated!
Just make ModelSpec collection observable (i.e. implement INotifyCollectionChanged yourself, or use ObservableCollection class for it).
Well, this is probably just another "inelegant" solution, but one more correct way would be to get the BindingExpression from the combo-box and call BindingExpression.UpdateSource.
Thanks for you assistance, in the end this did the trick and I prefer it to my first workaround.
It seems fine to me, but I guess others may gasp in horror? Please feel free to comment if so!
ModelSpecs is on my ManageVehicleViewModel so it dosnt seem that out of place to have the extra PropertyChanged call.
private IEnumerable<ModelSpec> _modelSpecs;
public IEnumerable<ModelSpec> ModelSpecs
{
get
{
return _modelSpecs;
}
set
{
if (_modelSpecs == value) return;
_modelSpecs = value;
OnPropertyChanged("ModelSpecs");
OnPropertyChanged("Vehicle");
}
}