Archive

Posts Tagged ‘Binding’

Gotcha: .NET Point and PointConverter inconsistency in string format used

I have submitted the following issue to Microsoft Connect (product feedback center):

http://connect.microsoft.com/VisualStudio/feedback/details/809084/point-class-issue-with-two-way-databinding

Point class issue with two-way databinding

 

In Silverlight, when using databinding code like the following:
<StackPanel Orientation="Vertical" Name="propPosition">
    <sdk:Label Style="{StaticResource PropertyLabelStyle}" Content="Position:" Target="{Binding ElementName=edPosition}" />
     <TextBox Name="edPosition" Text="{Binding Position, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=true}" />
</StackPanel>

and binding (via DataContext) to a View that has the following property:

Point Position { get; set; }

then what you see on the UI is values like 500; 100

but when you try to edit the position and enter say 400; 100 it shows a red message that it is invalid format (caught an exception that is and showing on the UI automatically cause of ValidatesOnExceptions and NotifyOnValidationError being true)
if you enter 400, 100 it works ok (it moves a ClipFlair Studio [http://ClipFlair.net] window arround in my case), so it outputs the two numbers (X, Y) with a ";" but expects to get them separated with a "," (also wonder if it copes OK with numbers in say format used in Greece where decimal point separator is , instead of .)

 

I managed to reproduce the same behaviour in a .NET console program:

//Console program in C# to demonstrate Point.ToString and PointConverter (from string) inconsistency in string format
//(the former outputs X;Y, the later expects X,Y)

using System;
using System.Windows; //for "Point" class (needs WindowsBase.dll reference)

namespace ConsoleApplication1
{
  class Program
  {

    static PointConverter conv = new PointConverter();

    static void Main(string[] args)
    {
      Point p = new Point(10, 20);
      Console.WriteLine(p); //outputs "10;20"
           
      //——————

      Point p1 = (Point)conv.ConvertFrom("10, 20");
      Console.WriteLine(p1); //outputs "10;20"

      //——————

      Point p2 = (Point)conv.ConvertFrom("10; 20"); //unhandled exception "System.FormatException" in mscorlib.dll
      Console.WriteLine(p2); //never reached cause of exception above
    }

  }
}

Advertisements

HowTo: Bind ASP.net control to list of files or folders

At ClipFlair Gallery metadata input pages for Activities and Clips I had to bind an ASP.net control to a list of files and folders respectively and although I found a Folder Contents DataSource control, it didn’t cover my needs (like filtering of a folder contents).

I just contributed my solution using .NET Anonymous Types and LINQ (assumes a using System.Linq clause) to:

http://stackoverflow.com/questions/1331793/bind-repeater-to-a-list-of-files-and-or-folders

private string path = HttpContext.Current.Server.MapPath("~/activity");

   protected void Page_Load(object sender, EventArgs e)
   {      
if (!IsPostBack) { //only at page 1st load
listItems.DataSource = Directory.EnumerateFiles(path, "*.clipflair") .Select(f => new { Filename=Path.GetFileName(f) });
listItems.DataBind(); //must call this } }

The above snippet gets all *.clipflair files from ~/activity folder of a web project

Update: using EnumerateFiles (availabe since .NET 4.0) instead of GetFiles since this is more efficient with LINQ queries. GetFiles would return a whole array of filenames in memory before LINQ had a chance to filter it.

The following snippet shows how to use multiple filters, which GetFiles/EnumerateFiles don’t support themselves:

private string path = HttpContext.Current.Server.MapPath("~/image");
private string filter = "*.png|*.jpg";

protected void Page_Load(object sender, EventArgs e)
{
  _listItems = listItems; 
  
  if (!IsPostBack)
  {
    listItems.DataSource =
      filter.Split('|').SelectMany(
oneFilter => Directory.EnumerateFiles(path, oneFilter)
.Select(f => new { Filename = Path.GetFileName(f) })
); listItems.DataBind(); //must call this if (Request.QueryString["item"] != null) listItems.SelectedValue = Request.QueryString["item"];
//must do after listItems.DataBind } }


The snippet below shows how to get all directories from /~video folder and also filters them to select only directories that contain a .ism file (Smooth Streaming content) with the same name as the directory (e.g. someVideo/someVideo.ism)

using System;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;

namespace ClipFlair.Gallery
{
 public partial class VideoMetadataPage : System.Web.UI.Page
 {

  private string path = HttpContext.Current.Server.MapPath("~/video");

  protected void Page_Load(object sender, EventArgs e)
  {
    if (!IsPostBack) { //only at page 1st load
listItems.DataSource = Directory.GetDirectories(path) .Where(f =>(Directory.EnumerateFiles(f,
Path.GetFileName(f)+".ism").Count()!=0)) .Select(f => new { Foldername = Path.GetFileName(f) }); //when having a full path to a directory don't use Path.GetDirectoryName //(gives parent directory), //use Path.GetFileName instead to extract the name of the directory listItems.DataBind(); //must call this } }

 

The examples above are from a DropDownList, but it’s the same logic with any ASP.net control that supports Data Binding (note I’m calling Foldername the data field at the 2nd snippet and Filename at the 1st one, but could use any name, need to set that in the markup):

        <asp:DropDownList ID="listItems" runat="server" AutoPostBack="True" 
          DataTextField="Foldername" DataValueField="Foldername" 
          OnSelectedIndexChanged="listItems_SelectedIndexChanged"
          />

Fix: Silverlight Media Framework Player VolumeElement out of sync

I was just adding a storable (persistent) Volume property to the MediaPlayerView class used at ClipFlair’s MediaPlayerWindow (connected to the underlying SMF player’s VolumeLevel property), when I realized that after reloading saved state, the SMF player’s Volume control would show a different value than the value set to it (which I could confirm by flipping the MediaPlayer control and looking at its properties on the backpanel that the ClipFlair app features for each component and for the activity container itself).

It seems to be some bug in the SMF logic or in its default template, since the following fix makes the issue disappear. The trick is that at a descendent class from SMFPlayer (like the MediaPlayer class that ClipFlair’s MediaPlayerWindow uses), one can override the OnApplyTemplate method (a standard method in templated XAML-based controls at Silverlight and WPF) and call the UpdateVolumeElement method shown below.

That method temporarily sets the volume to another value than its current one (this is important, just setting again to the same value would be ignored), then set again the current value to force the VolumeElement control’s UI to update.

 

//Project: ClipFlair (http://ClipFlair.codeplex.com)
//Filename: MediaPlayer.cs
//Version: 20130211

using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;

using Microsoft.SilverlightMediaFramework.Core;
using Microsoft.SilverlightMediaFramework.Core.Media;
using Microsoft.SilverlightMediaFramework.Core.Accessibility.Captions;
using Microsoft.SilverlightMediaFramework.Plugins.Primitives;

namespace ClipFlair.MediaPlayer
{

  public class MediaPlayer : SMFPlayer
  {

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      //...
      UpdateVolumeElement(); //patch for SMF bug
    }

    protected void UpdateVolumeElement()
    {
      //patch for SMF to update VolumeElement UI with any already set VolumeLevel
      double volume = VolumeLevel;
      VolumeLevel = (volume == 1) ? 0.9 : 1;
      VolumeLevel = volume;
    }

//...

 

Update: While submitting this as a bug to SMF source site on Codeplex it came to me that I could try setting the VolumeElement’s VolumeLevel value directly at ApplyTemplate and indeed it works:

protected void UpdateVolumeElement() 
{ //patch for SMF to update VolumeElement UI with any already set VolumeLevel VolumeElement.VolumeLevel = VolumeLevel; }

HowTo: Bind cell to row data in DataGrid DataTemplate for MVVM control

When using MVVM design style for a Silverlight or WPF control, it is common to use the control’s DataContext to keep the associated ViewModel instance, so that in the XAML file for the control one can use simple binding statements from the UI to the view model (and back if TwoWay binding mode is specified) of the form:
Text=”{Binding Comments}”
or
Text=”{Binding Comments, Mode=TwoWay}”

However, if you use that pattern you will fall into trouble when using your control inside a DataTemplate for a DataGrid Cell, when you use the same simple binding statements to bind properties of your control to a row cell of data (passed to you by DataGrid, which gets them from its [bound] data source).

The reason is that binding statements inside DataTemplates seem to be resolved at the context of your control and not at the context of say the DataGrid, which, combined with the fact that your control won’t be inheriting its parent’s (the DataGrid Cell’s that is) DataContext, since it has a non-null DataContext value of its own (where it keeps its ViewModel instance), results in the binding expression not working properly.

You can set a breakpoint in the XAML at the row of the data binding expression and examine the binding context at runtime via the locals window in Visual Studio, but if it happens that you have the same property name at your control, at its ViewModel and also at the row of data from the datasource (as I am doing with “Audio” used at the ResourceDictionary XAML excerpt from ClipFlair shown below), then it’s hard to spot that you’ve been binding your control’s dependency property to a field of its own ViewModel, instead of binding it to the DataGrid source’s row cell data.

The best workaround is to use a RelativeSource Binding like below (compare the Audio and the Comments templates there – note that at the Audio we don’t need a separate edit template, our control is live and “editing” [recording/playing audio] at any time)

  <!-- Audio -->
  
  <DataTemplate x:Key="AudioCellTemplate">
    <audio:AudioRecorderControl 
Audio="{Binding RelativeSource={RelativeSource
AncestorType=FrameworkElement},
Path=DataContext.Audio, Mode=TwoWay}" />
<!-- AudioRecorderControl sets its DataContext to its ViewModel,
so it doesn't inherit its parent's DataContext -->
</DataTemplate> <!-- Comments --> <DataTemplate x:Key="CommentsCellTemplate"> <TextBlock Margin="4" Text="{Binding Comments}" /> </DataTemplate> <DataTemplate x:Key="CommentsCellEditTemplate"> <TextBox Margin="4" AcceptsReturn="True"
Text="{Binding Comments, Mode=TwoWay,
ValidatesOnExceptions=True, NotifyOnValidationError=true}" /> </DataTemplate>

Note that the “{Binding … }” strings above need to be in a single line in the XAML file

References:

for WPF: http://stackoverflow.com/questions/3404707/access-parent-datacontext-from-datatemplate/13554039

for Silverlight: http://www.wintellect.com/cs/blogs/sloscialo/archive/2011/07/26/where-s-my-datacontext.aspx

HowTo: Bind to a DataContext property named Source in XAML

While refactoring ClipFlair code to use MVVM (Model-View-ViewModel) pattern, I came across the XAML error “Object reference not set to an instance of an object.”, shown in Visual Studio when trying to bind to my ViewModel (accessed implicitly, being set as the DataContext of the XAML control) for a property named Source.

I have settled down to using the following MVVM-style pattern (of my own) at ClipFlair:

image

Update: Later on, at ClipFlair project, I renamed folder “Views” to “ViewModels” and folder “Components” to “Views” and moved “ViewModels” folder inside the “Views” one to keep view-related stuff in one place.

IImageViewer.cs:

image

ImageView.cs:

image

Update: Later on, I refactored this to initialize the fields directly instead of at the constructor:

//can set fields directly here or at the constructor     
private Uri source = IImageViewerDefaults.DefaultSource;

ImageViewerWindow.xaml.cs (the so-called codebehind for the XAML control):

image

Note above how we set the “DataContext” of the control to our ViewModel, so that we can bind to its properties implicitly (without referencing it) in the XAML.

Update: There’s something very important I had forgotten to do at the implementation of the “View” property in the code above, that is to listen for PropertyChangeEvents emitted by the ViewModel. In specific, we need to listen for changes of the ViewModel’s “Source” property to keep the View’s “SourceProperty” (a DependencyProperty that is also accessed via the View’s “Source” property) in sync with the ViewModel’s “Source” property. So the code shown in the above screenshot has to be fixed by changing the “View” property’s implementation like below:

image

For extra safety in case you want to allow setting a null ViewModel to the View property, you could check for (value != null) before trying to add PropertyChanged event handler to it:

if (value != null)

  value.PropertyChanged += new PropertyChangedEventHandler(View_PropertyChanged);

Obviously, if more DependencyProperties are added to the component (e.g. to be able to set them declaratively from XAML when instantiating the component) that have to be in sync to respective ones at the ViewModel (which is held by the View property), then the “View_PropertyChanged” method (event handler) implementation has to be extended with “else if” statements for each of those properties to set the respective dependency properties from the matching ViewModel properties.

BTW, if your ViewModel passes null to the changed property name to mark a single change event for multiple properties, then you have to also take that in mind (else you will get exception when you try to call “Equals” method on null string), by doing something like below:

if (e.PropertyName == null) {

Source = View.Source;

OtherProperty = View.OtherProperty;

//…sync all properties since we are not told which ones changed

return;

}

else if (e.PropertyName.Equals(“Source”) {

Source = View.Source;

}

else if (e.PropertyName.Equals(“OtherProperty”)

{

OtherProperty = View.OtherProperty;

}

//…

Note that instead of “.Equals” one could have also used “==” operator, since the C# compiler maps the string equality operator to string’s “.Equals” method.

Update: Another retouch I have since done to the code above is to avoid hardcoding property names and their default values and use nstead ithe proprerty name constants defined at the class IImageViewerProperties and the respective default values defined at IImageViewerDefaults. For example, I prefer to use (after adding a “using ClipFlair.Models.Views;” to the top):

public static readonly DependencyProperty SourceProperty =

DependencyProperty.Register(IImageViewerProperties.PropertySource, typeof(Uri), typeof(ImageWindow), new FrameworkPropertyMetadata(

(Uri)IImageViewerDefaults.DefaultSource,

new PropertyChangedCallback(OnSourceChanged)));

Update: I eventually opted for using a “switch” statement instead of nested if / else if statements, to keep things cleaner when more properties are added:

    protected void View_PropertyChanged(object sender, 
                                        PropertyChangedEventArgs e)
    {
       if (e.PropertyName == null)
       {
         Source = View.Source;
         //...
       }
       else switch (e.PropertyName)
           //string equality check in .NET uses ordinal (binary) comparison 
           //semantics by default
         {
           case IImageViewerProperties.PropertySource:
             Source = View.Source;
             break;
           //...
         }
     }

In an even newer iteration of that code, I removed the if/else and added the null check in the switch statement:

    protected void View_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      switch (e.PropertyName) 
      {
        case null:
          Source = View.Source;
          //...
          break;
        case IImageViewerProperties.PropertySource:
          Source = View.Source;
          break;
        //...
       }
    }

ImageViewerWindow.xaml (the XAML control):

image

The issue at the last screenshot is that you can’t write Source=”{Binding Source}” as you’d write Source=”{Binding SomeDataContextProperty}”, but need to write Source=”{Binding Path=Source}” instead, since Source is a keyword at Binding expression syntax.

BTW, note that at the “edImageURL” TextBox I use Mode=TwoWay (two-way binding, the default is one way binding from source [the DataContext in our case] to target), whereas at the Image control I don’t. Both get an image URI from the ViewModel’s “Source” property, but we also want to be able to edit the URI at the textbox and update the view’s Source property (which will in turn update the Image control with the new image).

Also, note that apart from passing a URI to the Source property of an Image control there’s also a more verbose syntax one could use above, which can be handy to know of:

<Image>

<Image.Source>

<BitmapImage UriSource=”{Binding Path=Source}” />

</Image.Source>

</Image>

The <Image.Source> tag here is using the so-called property element syntax pattern. In the snippet above it sets a value to the Source property of the Image tag. XAML can use both XML attributes and child elements to set values to properties of controls, but whereas the attribute name would be “Source”, the child element tag has to be “Image.Source”, not just “Source”, since the XML schema needs to have unique definitions for the XML tags, whereas multiple objects could define different Source properties causing naming collisions.

According to http://10rem.net/blog/2012/03/27/tip-binding-an-image-elements-source-property-to-a-uri-in-winrt-xaml this more verbose syntax is needed at Windows 8 Metro-style apps (that is for the WinRT API) for binding an Image to a URI (but since that article is back from March 2012, probably this is not an issue any more).

%d bloggers like this: