2008-05-29

ASP MVC preview 3 released

I'm trying to upgrade from Preview 2 to Preview 3. I think the idea of having each action return an ActionResult was a good one, so far it has actually made my code slightly smaller.

What I don't understand though is why Html.Select seems to have disappeared...

Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.

Compiler Error Message: CS1501: No overload for method 'Select' takes '5' arguments

Source Error:

Line 8: Product
Line 9:
Line 10: <%= Html.Select("SoftwareID", (object)ViewData["SoftwareList"], "Name", "ID", (object)ViewData["SoftwareID"]) %>
Line 11:
Line 12:


When I go into the APX and type Html. there is no Select method listed along with the other options! Where is it?

2008-05-28

Hooking into ECO multi-association events

Although I have not (yet) needed this myself I can see myself needing it in the future and the question has been asked before.

"Setting HasUserCode=True on a Child.Parent single role does what I want, but how do I handle the scenario where Parent.Children.Add(item) is called on a multirole?"

By default you can’t, but with the addition of a single class and a small amount of tweaking you can get it to do what you want! Here is how to do it:


01: Mark Parent.Children’s association end with HasUserCode=True in the modeler and then generate code.
02: In the source code of your class (not within an ECO region) add the following


  private EcoMultiAssociation<Child> m_Children;


This is a class that does not yet exist, I will show the source code for it later.


02: In the source code locate the "Children" property and change it like so

  public IEcoList<Child> Children
  {
    get
    {
      if (m_Children == null)
      {
        m_Children= new EcoMultiAssociation<Child>((IList)(this.eco_Content.get_MemberByIndex(Eco_LoopbackIndices.Children_MemberIndex)));
        m_Children+= new AssociationItemChangedEventHandler<Child>(result_ItemChanged);
        m_Children+= new AssociationChangedEventHandler<Child>(result_ItemInserted);
        m_Children+= new AssociationChangedEventHandler<Child>(result_ItemRemoved);
      }
      return m_Children

#if NeverDoThis
      #region MM_ECO_Generated
      return new ObjectListAdapter<Child>((IList) (this.eco_Content.get_MemberByIndex(Eco_LoopbackIndices.Children_MemberIndex)));
      #endregion
#endif
    }
  }


Note that I have put an #if around the original code that will never be true. You cannot remove the MM_ECO_Generated section due to the source code generator expecting to find it, but you can make sure it is never even compiled! The parameters for the EcoMultiAssociation<T> constructor were just copied directly from the ObjectListAdapter constructor below.

(Just a small note, I don’t usually name my private members m_Name, I just did it in this case to make it easier to spot the difference between the Children property and the m_Children private member).


03: The event handlers are implemented like so

    void result_ItemRemoved(object sender, AssociationChangedEventArgs<Child> args)
    {
      System.Diagnostics.Debug.WriteLine(string.Format("Removed index {0}", args.Index));
    }

    void result_ItemInserted(object sender, AssociationChangedEventArgs<Child> args)
    {
      System.Diagnostics.Debug.WriteLine(string.Format("Inserted at index {0}", args.Index));
    }

    void result_ItemChanged(object sender, AssociationItemChangedEventArgs<Child> args)
    {
      System.Diagnostics.Debug.WriteLine(string.Format("Changed object at index {0}", args.Index));
    }


The args parameter has a reference to the old object and also the new object in the case of ItemChanged which is executed when you do this...

Association[x] = y;



Finally here is the source code for the EcoMultiAssociation<T> class. There’s quite a bit here, but that’s really because I have to implement so many interfaces, the actual code is very small.

  public delegate void AssociationChangedEventHandler<T>(object sender, AssociationChangedEventArgs<T> args);
  public class AssociationChangedEventArgs<T> : EventArgs
  {
    public readonly T Item;
    public readonly int Index;

    public AssociationChangedEventArgs(int index, T item)
    {
      Item = item;
      Index = index;
    }
  }

  public delegate void AssociationItemChangedEventHandler<T>(object sender, AssociationItemChangedEventArgs<T> args);
  public class AssociationItemChangedEventArgs<T> : AssociationChangedEventArgs<T>
  {
    public readonly T OriginalItem;

    public AssociationItemChangedEventArgs(int index, T newItem, T originalItem)
      : base(index, newItem)
    {
      OriginalItem = originalItem;
    }
  }


  public class EcoMultiAssociation<T> : IEcoList<T>, IList
  {
    private readonly IList Adaptee;

    public EcoMultiAssociation(IList adaptee)
    {
      if (adaptee == null)
        throw new ArgumentNullException("Adaptee");

      Adaptee = adaptee;
    }

    public void Add(T item)
    {
      if (Adaptee.IndexOf(item) == -1)
      {
        Adaptee.Add(item);
        OnItemInserted(Count - 1, item);
      }
    }

    public void Clear()
    {
      List<T> originals = new List<T>(this);
      Adaptee.Clear();
      for (int index = 0; index < originals.Count; index++)
        OnItemRemoved(index, originals[index]);
    }

    public bool Contains(T item)
    {
      return Adaptee.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
      Adaptee.CopyTo(array, arrayIndex);
    }

    public int Count
    {
      get { return Adaptee.Count; }
    }

    public IEnumerator<T> GetEnumerator()
    {
      return new ObjectEnumeratorAdapter<T>(Adaptee.GetEnumerator());
    }

    public int IndexOf(T item)
    {
      return Adaptee.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
      if (Adaptee.IndexOf(item) == -1)
      {
        Adaptee.Insert(index, item);
        OnItemInserted(index, item);
      }
    }

    public bool IsReadOnly
    {
      get { return Adaptee.IsReadOnly; }
    }

    public bool Remove(T item)
    {
      int index = Adaptee.IndexOf(item);
      if (index == -1)
        return false;

      Adaptee.RemoveAt(index);
      return true;
    }

    public void RemoveAt(int index)
    {
      T item = this[index];
      Adaptee.RemoveAt(index);
      OnItemRemoved(index, item);
    }

    public T this[int index]
    {
      get
      {
        return (T)Adaptee[index];
      }
      set
      {
        T originalItem = (T)Adaptee[index];
        Adaptee[index] = value;
        OnItemChanged(index, originalItem, value);
      }
    }

    public event AssociationChangedEventHandler<T> ItemInserted;
    protected void OnItemInserted(int index, T item)
    {
      AssociationChangedEventHandler<T> handler = ItemInserted;
      if (handler != null)
      {
        AssociationChangedEventArgs<T> args = new AssociationChangedEventArgs<T>(index, item);
        handler(this, args);
      }
    }

    public event AssociationChangedEventHandler<T> ItemRemoved;
    protected void OnItemRemoved(int index, T item)
    {
      AssociationChangedEventHandler<T> handler = ItemRemoved;
      if (handler != null)
      {
        AssociationChangedEventArgs<T> args = new AssociationChangedEventArgs<T>(index, item);
        handler(this, args);
      }
    }

    public event AssociationItemChangedEventHandler<T> ItemChanged;
    protected void OnItemChanged(int index, T originalItem, T newItem)
    {
      AssociationItemChangedEventHandler<T> handler = ItemChanged;
      if (handler != null)
      {
        AssociationItemChangedEventArgs<T> args = new AssociationItemChangedEventArgs<T>(index, newItem, originalItem);
        handler(this, args);
      }
    }


    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      return GetEnumerator();
    }

    #endregion

    #region IList Members

    int IList.Add(object value)
    {
      Add((T)value);
      return IndexOf((T)value);
    }

    bool IList.Contains(object value)
    {
      return Contains((T)value);
    }

    int IList.IndexOf(object value)
    {
      return IndexOf((T)value);
    }

    void IList.Insert(int index, object value)
    {
      Insert(index, (T)value);
    }

    bool IList.IsFixedSize
    {
      get { return Adaptee.IsFixedSize; }
    }

    void IList.Remove(object value)
    {
      Remove((T)value);
    }

    object IList.this[int index]
    {
      get
      {
        return this[index];
      }
      set
      {
        this[index] = (T)value;
      }
    }

    #endregion

    #region ICollection Members

    void ICollection.CopyTo(Array array, int index)
    {
      Adaptee.CopyTo(array, index);
    }

    bool ICollection.IsSynchronized
    {
      get { return Adaptee.IsSynchronized; }
    }

    object ICollection.SyncRoot
    {
      get { return Adaptee.SyncRoot; }
    }

    #endregion

  }



Note that these events will not be executed if you do Child.Parent = p; For this case you have to set HasUserCode=True on Child.Parent as normal and react accordingly.

2008-05-08

Using Vista Aero theme in XP WPF apps

I found this article on the web recently which shows how to use the Vista Aero theme on XP in your WPF apps.

I found two things:
01: It is less complicated that the article states.
02: It is a bit different if you already have stuff in your app resources, such as styles or control templates etc.

So here are my steps
01: Add PresentationFramework.Aero to your applications References list. It is listed in the [.NET] tab.
02: Edit your App.xaml and change it from this

<Application.Resources>
  <!-- Your stuff here -->
</Application.Resources>

to this

<Application.Resources>
  <ResourceDictionary>
    <!-- Put your stuff here instead -->

    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="/PresentationFramework.Aero;component/themes/aero.normalcolor.xaml"/>
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</Application.Resources>

2008-05-04

Selecting WPF ListView row in code

An app I am considering migrating to WPF uses a DevExpress grid to show a list of events, as I animate my composition I highlight the current event in the grid. Things I have learned about WPF:

01: You can create a data grid type view like so:
<ListView Name="ListView">
  <ListView.View>
    <GridView AllowsColumnReorder="False">
      <GridViewColumn DisplayMemberBinding="{Binding Path=Title}" Header="Title"/>
      <GridViewColumn DisplayMemberBinding="{Binding Path=FirstName}" Header="FirstName"/>
      <GridViewColumn DisplayMemberBinding="{Binding Path=LastName}" Header="LastName"/>
    </GridView>
  </ListView.View>
</ListView>


02: Don’t put a ListView in a <StackPanel>! It renders all of the data at once. When you have 6K rows of data for example it takes about 1 second to select the next row. Instead you should put it in a grid

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="50"/>
    <RowDefinition Height="*"/>
  </Grid.RowDefinitions>

  <Button Grid.Row="0"...../>
  <ListView Grid.Row="1"...../>
</Grid>

03: When selecting a row in code the row does not become visible. To automatically make the row visible you need to use ScrollIntoView()

  ListView.SelectedIndex++;
  ListView.ScrollIntoView(ListView.SelectedItem);

2008-05-01

Creating a drop shadow

Requirement: Take a PNG image that has an alpha mask and from it generate a new bitmap which has the same alpha mask but every pixel is black.

Solution:
private Bitmap GenerateDropShadowImage(Image image)
{
  Bitmap bitmap = new Bitmap(image);
  BitmapData bits = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

  IntPtr bottomScanLine = bits.Scan0;
  int bytesPerRow = bits.Width * 4;

  unsafe
  {
    byte* pixelValue = (byte*)bottomScanLine.ToPointer();
    for (int count = 0; count < bits.Width * bits.Height; count++)
    {
      pixelValue[0] = 0;
      pixelValue[1] = 0;
      pixelValue[2] = 0;
      pixelValue = pixelValue + 4;
    }
  }
  bitmap.UnlockBits(bits);
  return bitmap;
}