Fixing Tim Heuer's EditableComboBox for SL3 & latest SL Toolkit - silverlight

I'm using Tim Heuer's style to get an editable combo box from here:
http://timheuer.com/blog/archive/2008/11/05/silverlight-editable-combobox-using-styles.aspx
This is working well in my project circa the previous release of the Silverlight Toolkit. Unfortunately, attempting to use this style with SL3 RTM and the latest SL Toolkit doesn't work. I suspect that the problem has to do with this: "Breaking Change: The "DropDownToggle" template part of type ToggleButton has been removed." I'm not sure how to fix it to get the drop down part to work again, any ideas?

For me it works now. I added this line to ToggleButton declaration in EditableComboStyle template:
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropDownOpen, Mode=TwoWay}"
Also it is important to change the ListBox name to x:Name="Selector" in the same EditableComboStyle template like Jeff said. Then the control is used as:
<toolkit:AutoCompleteBox MinimumPrefixLength="0"
MinimumPopulateDelay="200"
x:Name="editableCombo"
Style="{StaticResource EditableComboStyle}"
Text="{Binding MyProperty, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" />
But unfortunately when the desired editable combobox behavior works, other issues appears:
I have problem with immediate binding when user click on toogle button which causes immediate validation before any item is selected or text written.
User have to press tab twice for move to other control.

If you take a look inside of the Silverlight Toolkit C# samples (for the latest July 2009 release), you'll see that the editable combo box sample has changed some. You are correct that the breaking change is the reason for this.
The ToggleButton still exists in the custom template for the control, but you need to hook up to the Click event in your code-behind file, and actually toggle the IsDropDownOpen value in your code.
This change was made to be consistent: having the control support template parts that are not in the default control template was decided to be against good design guidelines for controls, so I removed it for the final release of Silverlight 3.
I'm sorry it's caused some trouble, and hope that updating your application is easy enough!
Also, be aware that in any custom control templates, with the July 2009 release of the Silverlight Toolkit for Silverlight 2 (or the Silverlight 3 SDK), the AutoCompleteBox's ListBox template part has been renamed to "Selector" from "SelectionAdapter", so you may need to rename your ListBox template part in your own styles.

I had a bit of trouble putting the fixes together for SL3 (I couldn't find it in one place) so for anyone else I will leave my working version here:
http://walkersretreat.co.nz/files/SLComboEdit3.zip
This includes the fix for the tabstop.
There is nothing new in here - just put together what others have said in one place.

Khyalis on Tim Heuer's blog seems to have fixed this by binding IsChecked on the DropDownToggle:
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropDownOpen, Mode=TwoWay}"
So far it works for me.

IsTabStop=false on the AutoCompleteBox will fix the tab issue.
The TextBox (inside the AutoCompleteBox template) will still get focus on tab.

Related

Converters are being called on every keystroke rather than at the end of user input

I'm having a problem with data entry since switching to .NET 4.0.
In my Xceed 3.7 grid, the user used to be able to type a value into a cell, and when they clicked away or hit enter, the bound converter's ConvertBack method would be called, parsing the user input value and storing in the desired format.
Now all of the sudden, this is happening every keystroke - which is causing a huge problem, because if the user erases a number and starts typing another one, (lets say -100), as soon as they type the negative sign, the convertback fires and throws an exception because "-" is not a parsable string, and the value is reverted.
I think the problem is pretty clear, so now I'll paste in some code.
Columns for user input appear as follows:
<xcdg:DataGridControl x:Name="AggCatGrid01"
ItemsSource="{Binding Source={StaticResource myDataSource}}" >
<xcdg:DataGridControl.Columns>
...
<xcdg:Column VisiblePosition="0" FieldName="SomeValue" Title="Some Value"
CellEditor="{StaticResource PercentageEditor}"
CellContentTemplate="{StaticResource EditablePercent2CellContentTemplate}" />
Datagrids all share the same style:
<Style x:Key="{x:Type xcdg:DataGridControl}" TargetType="{x:Type xcdg:DataGridControl}">
<Setter Property="UpdateSourceTrigger" Value="CellEndingEdit"/>
<Setter Property="AutoCreateColumns" Value="False"/>
<Setter Property="EditTriggers" Value="BeginEditCommand, CellIsCurrent, ActivationGesture"/>
<Setter Property="CellEditorDisplayConditions" Value="CellIsCurrent"/>
<Setter Property="NavigationBehavior" Value="CellOnly"/>
Notice that UpdateSourceTrigger is set to CellEndingEdit. I would have thought that this right here would be responsible for when the converters get called and the bound value is updated. Whatever controls that changed just by switching .NET4 though.
Here's the data template for the column you saw used above:
<!-- Styles used when editable cells are being edited. -->
<xcdg:CellEditor x:Key="PercentageEditor">
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<xcdg:AutoSelectTextBox Style="{StaticResource DefaultAutoSelectTextBox}"
Text="{xcdg:CellEditorBinding Converter={StaticResource EditablePercentageConverter}}" />
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
I think the converter code itself is irrelevant, so I'll leave it out unless it's requested. The problem is that it's getting called every keystroke.
If you can shed any light on this, I'd be ecstatic. I mean, I might have to roll back all my .NET 4.0 enhancements, or delay my next release by upwards of a month rewriting all my datagrids to no longer use xceed if there isn't a solution to this. Thanks guys.
Update #1
I actually came up with a moderately clever workaround (in my modest opinion) where I introduce a dummy textblock to hold the CellEditorBinding xceed forces us to use in the datatemplate. I then changed my input control to bind to the textblock's text property rather than the CellEditorBinding directly, which allowed me to specify my own binding mode. Here I was able to set the mode to 'lostFocus' and the major problem was solved! Converter is no longer being called on every keystroke, but only when the user leaves the cell or hits enter.
<xcdg:CellEditor x:Key="PercentageEditor">
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<Grid>
<TextBlock x:Name="bind_source" Text="{xcdg:CellEditorBinding}" Visibility="Collapsed"/>
<xcdg:AutoSelectTextBox Style="{StaticResource DefaultAutoSelectTextBox}"
Text="{Binding ElementName=bind_source, Path=Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus, Converter={StaticResource EditablePercentageConverter}}" />
</Grid>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
As you can imagine though, this layer of indirection has cause a few other minor issues, such as breaking validation. Oddly enough, now when the user types invalid data, the converter throws an exception which xceed catches and uses to turn on the cell's error template, but correcting the error and hitting enter no longer works. The user's only option is to hit the ESC key, causing the cell value to revert and lose focus, before they can correct their entry.
I'm still hoping for a more elegant solution that will fix this.
Update #2
I found a developer on Xceed support forums which presented the same issue as me in this post: http://silverlightdatagrid.com/CS/forums/permalink/31548/31516/ShowThread.aspx#31516.
Many users seem totally confused by your examples (which are largely out of date for .Net 4.0) and only target your own controls using the xcdg:CellEditorBinding which only seems to support PropertyChanged validation.
Unfortunately no solution was ever offered. He did present a strategy for changing the update source trigger more elegantly which I was able to adopt, but I still have the problem of the validation error freezing the cell until the user hits ESC.
<xcdg:AutoSelectTextBox Style="{StaticResource DefaultAutoSelectTextBox}"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=xcdg:DataCell},
Path=Content, UpdateSourceTrigger=LostFocus,
Converter={StaticResource EditablePercentageConverter}}" />
Update #3
I have confirmed that by updating to Xceed DataGrid version 4.3 (on trial), the problem went away all on its own, as in that version, Xceed updated its xcdg:CellEditorBinding UpdateSourceTrigger incompatibility with .Net4.0. Since, however, a license for Xceed only includes 6 months of bug fix updates before you have to pay for a whole new license (ridiculous), and I don't see any company authorizing an outrageous $1200 single developer license fee to use the latest Xceed dll just for this one minor bug, I'm still going to strive to find a complete workaround in the 3.7 version of Xceed. I'll present this 'solution' for the developers that have access to money to burn.
As it turns out, upgrading to 4.3 didn't solve the problem. It only appeared to because I'd forgotten to back out my previous change. Even in the latest version, Xceed still hasn't exposed the UpdateSourceTrigger property on on CellEditorBinding.
The solution is:
<xcdg:AutoSelectTextBox Style="{StaticResource DefaultAutoSelectTextBox}"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=xcdg:DataCell},
Path=Content, UpdateSourceTrigger=LostFocus,
Converter={StaticResource EditablePercentageConverter}}" />
There's really no other way to do it. If you aren't using the latest version of Xceed, this will also lead to validation errors, but in the latest version, using this new binding path works perfectly. I still think it's a hack, and hopefully xceed will realize it needs to expose a few more properties on its CellEditorBinding.
I don't know XCeed controls, so this is just educated guess.
I personally would place the UpdateSourceTrigger to the binding declaration like it is done in a regular .NET control:
Text="{xcdg:CellEditorBinding Converter={StaticResource EditablePercentageConverter}, UpdateSourceTrigger=CellEndingEdit}"
Also, since the control is commercial, you should be entitled to some tech support from XCeed.

ShowDuration attribute cannot resolve in tooltip at silverlight 4

i want use to ShowDuration attribute in a tooltip at silverlight project. but ShowDuration not exist in ToolTip control
for example:
<Button x:Name="btnAppUserRoleAdd" Style="{StaticResource GlassButton}" Content="button" Width="100" Height="36" Margin="0 0 40 0">
<ToolTipService.ToolTip>
<ToolTip ShowDuration="10000" Template="{StaticResource ToolTipTemplate}" HorizontalOffset="2" VerticalOffset="5">
<ToolTip.Content>
<TextBlock Text="any test"
TextWrapping="Wrap" Style="{StaticResource ButtonTooltipFontStyle}" />
</ToolTip.Content>
</ToolTip>
</ToolTipService.ToolTip>
"ShowDuration="10000" this attribute have not known by intellisence in VS2010 and that say: cannot resolve symbol ShowDuration
my tooltip without ShowDuration working properly and not problem. but i want use this attribute. of course, i use tooltip class in code (c#) but problem not solved.
please help me
i use silverlight4, visual studio 2010.
The Silverlight implementation of a ToolTip does not have a ShowDuration property.
The referenced Tooltip opensource lib has big problem when use with Silverlight DataGrid control, it open many tooltips which are not closed when mouse moved away, it can open many of the same tooltips if you you move mouse arround in a cell. We are forced to take it out from project. We are still looking for a solution just to extend the display duration of tooltips.
Here is a Advanced Silverlight Tooltip Control. This Tooltip has the DisplayTime property. Maybe it is helpful.
You have to download the dll file and reference it.
Silverlight tooltips currently don't have the extended functionality that WPF provide to set delay or duration.
As much as I'd like to see this functionality baked into Silverlight, I've had to build something myself. This also meant I had to create my own ToolTipService as lots of the code I needed to hook into was internal.
Replace your references to ToolTipService and ToolTip to the ones in the library and you'll get more properties to exploit :-)
You can find it on Codeplex as well as on NuGet.
Hope that helps!
Cheers,
Xavier

XamlParseException using Silverlight Toolkit control in Expression Blend

I am having a strange issue opening up my UserControl in Expression Blend when using a Silverlight Toolkit control. My UserControl uses the toolkit's ListBoxDragDropTarget as follows:
<controlsToolkit:ListBoxDragDropTarget mswindows:DragDrop.AllowDrop="True" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ListBox ItemsSource="{Binding MyItemControls}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</controlsToolkit:ListBoxDragDropTarget>
Everything works as expected at runtime and looks fine in Visual Studio 2008. However, when I try to open my UserControl in Blend I get XamlParseException: [Line: 0 Position: 0] and I can not see anything in the design view. More specifically Blend complains:
The element "ListBoxDragDropTarget" could not be displayed because of a problem with System.Windows.Controls.ListBoxDragDropTarget: TargetType mismatch.
My silverlight application is referencing System.Windows.Controls.Toolkit from the Nov. 2009 toolkit release, and I've made sure to include these namespace declarations for the ListBoxDragDropTarget:
xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
xmlns:mswindows="clr-namespace:Microsoft.Windows;assembly=System.Windows.Controls.Toolkit"
If I comment out the ListBoxDragDropTarget control wrapper and just leave the ListBox I can see everything fine in the design view without errors. Furthermore, I realized this is happening with a variety of Silverlight Toolkit controls because if I comment out ListBoxDragDropTarget and replace it with
<controlsToolkit:BusyIndicator />
the same exact error occurs in Blend. What is even weirder is that if I start a brand new silverlight application in blend I can add these toolkit elements without any kind of error, so it seems like something dumb that is happening with my project references to the toolkit assemblies.
I'm pretty sure this has something to do with loading the default styles for the toolkit controls from its generic.xaml, since the error has to do with the TargetType and Blend is probably trying to load up the default styles.
Has anyone encountered this issue before or have any ideas as to what may be my problem?
Hi we had exactly the same issue, we solved it by checking the references in the project which was having this problem. All referenced toolkit assemblies should be in the same directory on disk.
Our project reference to the System.Windows.Controls.Toolkit.dll always 'jumped' back to the orginal path which was causing our issue. We solved by editing the project file in notepad++ (or any of your favorite text-editor) and hardcode the path where it could find the assembly.
Hope this helps.
For Visual Studio (maybe Blend too) you need to add a reference to :
System.Windows.Controls.Toolkit.Internals.dll
"C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10\Bin\System.Windows.Controls.Toolkit.Internals.dll"
I'm using Silverlight 5 toolkit and having XamlParseException when using BusyIndicator toolkit control in Expression Blend for SL 5, all above solutions didn't helped but I found another workaround, its rather dirty but allows me to make BusyIndecator work in Expression Blend,
derive from control
public class BusyIndicatorEx : BusyIndicator
{
public BusyIndicatorEx()
{
this.DefaultStyleKey = typeof(BusyIndicatorEx);
}
}
create style for derived control (just copy style from BusyIndicator source code to themes/generic.xaml, and change target type to local:BusyIndicatorEx)
I had the same problem which has been resolved by introducing a dummy reference in code behind to the Wrap Panel.
I know it is a little C++y but I can imagine it is because we have indirect reference to wrap panel inside a template and not on the top level page so the loader don't know what to load on initialization. I really like understand the exact reason, though.
I just referenced System.Windows.Controls.Toolkit and introduce following member in code behind:
System.Windows.Controls.WrapPanel _dummy;

Reclaiming the space from the DescriptionViewer part of the DataFields on a Silverlight Toolkit DataForm

The DescriptionViewer part of the DataField is used to display the Description property of the System.ComponentModel.DisplayAttribute as a ToolTip in the generated form. I don't want to use this capability and although I can make sure the UI element is not visible by using a style to set either the DescriptionViewerVisibility to Collapsed or by setting the DescriptionViewerStyle to be null as shown below, there is still space reserved in the DataField layout for this element.
<Style x:Key="DataFieldStyle1" TargetType="dataFormToolkit:DataField">
<Setter Property="DescriptionViewerVisibility" Value="Collapsed"/>
<Setter Property="DescriptionViewerStyle" Value="{x:Null}" />
</Style>
This space is as waste in my scenario and I want to get rid of it. I would expect this layout to be exposed by the DataField.Template property but when I use Blend to edit a copy of the default template the layout is not there.
I'm using the System.Windows.Controls.Data.DataForm.Toolkit, Version=2.0.5.0 from the October 2009 release of the Silverlight Toolkit within a WCF RIA Services Beta Business Application Silverlight 3 project. I'm using Visual Studio 2008 SP1. I know there is a November 2009 release but I can't see any mention of this changing in the release notes.
An alternative solution is to use DataForm Label and a control to display your field.
Instead of using a DataField like this and eventually having space for DescriptionViewer
<dataControls:DataField>
<TextBox Text="{Binding FirstName, Mode=TwoWay}" />
</dataControls:DataField>
You can use this code, and you will not have the DescriptionViewer
<dataInput:Label Target="{Binding ElementName=tbFirstName}" />
<TextBox x:Name="tbFirstName" Text="{Binding FirstName, Mode=TwoWay}" />
With this solution, you will loose the generated layout that come with the DataForm but you can do it easily with a simple Grid
Using Reflector I can see that the DataField.OnApplyTemplate method calls a private method called GenerateUI, which uses conventional code to create a Grid with a Column for the DescriptionViewer, and I can't see a way to prevent this, without doing some very low level .NET clr kind of hack which would be inappropriate. Am I missing something here?
I'm starting to come to the conclusion that you either need to stick very close to the default behaviour of these Silverlight Toolkit controls if you want to benefit from the supposed productivity gains. Anything more that pretty trivial customisation seems to be an incomplete story at the moment.

PropertyValueEditor and DependencyObject in Blend 3 - Silverlight DesignTime support

I'm working on a set of controls that has a number of DependencyProperties. The properties are themselves DependencyObjects and created during the get method of the properties. During the Get method, they are also set back to the propertybag using the SetValue() method, so they are in fact valid in Xaml and their properties can be storyboarded without having to explicitly created in the the visual tree.
These DependencyObjects has all its properties as DependencyProperties as well, for supporting DataBinding. They are as mentioned above possible to use in Storyboards.
At the same time I'm developing special designtime support for Blend 3 for these properties and have created an InlineEditorTemplate in the form of a Control. I create the template and set it for the PropertyValueEditor like this:
var vectorEditControl = new FrameworkElementFactory(typeof (VectorEditorControl));
var dataTemplate = new DataTemplate {VisualTree = vectorEditControl};
InlineEditorTemplate = dataTemplate;
In the Control I have the following:
<Grid DataContext="{Binding Value}">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=X, Mode=TwoWay}"/>
<TextBox Text="{Binding Path=Y, Mode=TwoWay}"/>
<TextBox Text="{Binding Path=Z, Mode=TwoWay}"/>
</StackPanel>
</Grid>
The editor shows up and I can edit the data. And even while debugging, I see that it actually sets the data back to the DependencyProperties on the DependencyObjects, but nothing happens to the Xaml. So the data is actually not persisted in any way in the Xaml and lost when I close the Xaml file and open it again.
Is there anything I need to do specifically for it to actually get into the Xaml? I was under the impression that this would happen automatically?
Excellent Question!
The core issue you're running into a misunderstanding as to what PropertyEditors in Blend/Cider end up databinding to.
Consider this object graph:
- MyControl
-- MyControl.MyProperty
--- FooClass
---- FooClass.BarProperty
Let's look at a scenario where we have a PropertyEditor (of any type: Inline, Dialog or Extended) to property MyControl.MyProperty.
When inside MyPropertyPropertyEditor you'd expect to get a fully settable copy of FooClass and be able to manipulate it's members.
That's a good assumption, but the wrong one.
The core issue is that Blend/Cider have elaborate data structures that represent your model at design time. There's about 3-5 levels of abstraction in how Blend/Cider interact with an actual control.
Creating those levels of abstraction allows Expression Blend / Visual Studio designers to be leveraged between framewroks (Silverlight / WPF) and support advanced scenarios (like Property transactions and property chaining).
So, the value you actually get to DataBind to is just one of those levels of abstraction.
Don't believe me? In your custom PropertyEditor, register for this.DataContextChanged event and checkout the type in this.DataContext. You'll end up getting the PropertyValue class (or one of it's friends).
Every single property change you want persisted to XAML (and shown on the design surface) should go through those abstraction layers.
the question you have to ask yourself is "Where do I get one of these absteaction classes for my PropertyValue.Value property instance?".
Well, what I'd do if I were you is create a ModelItem around MyControl.MyProperty and set that as your PropertyEditor.DataContext.
We've shipped an example of using ModelFactory.CreateItem in the Silverlight Toolkit as part of the Chart DefaultInitializer: Source Code, Ning Zhang (Awesome Design Time Dev) explains about ModelItem
If you've got follow-up questions I'd consider pinging PeteBl or UnniR through the Silverlight Insiders mailing list.
Sincerely,
-- Justin
It partly solves my problem. I'm having a dialog with UnniR for a followup.
I couldn't see how I could use this together with the PropertyValueEditor, but for default values this is brilliant and something I'll implement ASAP.
Thanks.

Resources