以前写过几个方法实现这个功能,但最终还是选择了不继承DataGrid,所以再重新发布出来。

先显示最终结果,其中左边是错误的例子,右边才是正确的

在DataGrid中使用CheckBox选择行时典型的错误就是CheckBox没有Binding到任何属性上,这样的话当拖动滚动条时CheckBox.IsChecked就会乱掉,如Demo中左边那个DataGrid所示。最直观的解决方法是禁用DataGrid的滚动条,或者在绑定的数据上添加一个用于绑定CheckBox的bool属性。
其实只要在DataGrid.LoadingRow事件中将CheckBox的DataContext设定为另外一个Object,就不需要牺牲DataGrid的高效能,也不需要改变原有数据的结构。最终的实现方法是自定义一个包含DataGrid的DataGridTemplateColumn,而不需要重写DataGrid,这样的坏处就是每次调用需要多写一局代码,如果不想这样就继承DataGrid然后把这句代码写进去。在DataGrid.Columns中加入自定义的Column,然后在代码中关联DataGrid和这个Colum,就完成了:

 

 <sdk:DataGrid ItemsSource="{Binding}"
                      AutoGenerateColumns
="False"
                      Grid.Column
="1"
                      x:Name
="TestDataGrid">
            <sdk:DataGrid.Columns>
                <local:DataGridSelectColumn />
                <sdk:DataGridTextColumn Header="Name"
                                        Binding
="{Binding Name}"
                                        Width
="*" />
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>

 

   TestDataGrid.EnableSelect();

 

下面是这个DataGridSelectColumn的具体实现。创建一个UserControl然后把它改成DataGridTemplateColumn,这样好处是DataTemplate和Style中的FrameworkElement都可以轻松地获取。

 

<sdk:DataGridTemplateColumn xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
                            x:Class
="DataGridSelectSample.DataGridSelectColumn"
                            xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                            xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns:d
="http://schemas.microsoft.com/expression/blend/2008"
                            xmlns:mc
="http://schemas.openxmlformats.org/markup-compatibility/2006"
                            mc:Ignorable
="d" 
                            d:DesignHeight
="300"
                            d:DesignWidth
="400">
    <sdk:DataGridTemplateColumn.HeaderStyle>
        <Style TargetType="ContentControl">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <CheckBox  Content="Select All"
                                   VerticalContentAlignment
="Center"
                                   VerticalAlignment
="Center"
                                   Loaded
="OnHeaderCheckBoxLoaded"/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </sdk:DataGridTemplateColumn.HeaderStyle>
    <sdk:DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay}"
                      VerticalAlignment
="Center"
                      HorizontalAlignment
="Center" />
        </DataTemplate>
    </sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>

 

 

public partial class DataGridSelectColumn : DataGridTemplateColumn
    {

        private DataGrid _ownerDataGrid;
        private CheckBox _headerCheckBox;
        private Dictionary<object, MarkObject> _markObjects;

        public event EventHandler SelectedItemChanged;

        public DataGridSelectColumn()
        {
            InitializeComponent();
            IsReadOnly = true;
            _markObjects = new Dictionary<object, MarkObject>();
        }

        public DataGrid OwnerDataGrid
        {
            get { return _ownerDataGrid; }
            set
            {
                _ownerDataGrid = value;
                _ownerDataGrid.LoadingRow += OnLoadingRow;
            }
        }

        public void SelecteAll()
        {
            if (_headerCheckBox != null)
                _headerCheckBox.IsChecked = true;
            SetAllSelectedStates(true);
        }

        public void UnselectAll()
        {
            if (_headerCheckBox != null)
                _headerCheckBox.IsChecked = false;
            SetAllSelectedStates(false);
        }

        public List<T> GetSelectedItems<T>()
        {
            List<T> result = new List<T>();
            if (_ownerDataGrid.ItemsSource != null)
            {
                var enu = _ownerDataGrid.ItemsSource.GetEnumerator();
                while (enu.MoveNext())
                {
                    if (GetMarkObject(enu.Current).IsSelected)
                        result.Add((T)enu.Current);
                }
            }
            ClearItems();
            return result;
        }

        public void SetSelectedItems(IList items)
        {
            if (_ownerDataGrid.ItemsSource == null)
                return;

            var enu = _ownerDataGrid.ItemsSource.GetEnumerator();
            while (enu.MoveNext())
            {
                GetMarkObject(enu.Current).IsSelected = items.Contains(enu.Current);
            }
        }

        private void ClearItems()
        {
            var enu = _ownerDataGrid.ItemsSource.GetEnumerator();
            List<object> list = new List<object>();
            while (enu.MoveNext())
            {
                list.Add(enu.Current);
            }
            List<object> removableObjects = new List<object>();
            foreach (var item in _markObjects)
            {
                if (list.Contains(item.Key) == false)
                {
                    removableObjects.Add(item.Key);
                }
            }
            for (int i = 0; i < removableObjects.Count; i++)
            {
                _markObjects.Remove(removableObjects[i]);
            }
        }

        private void OnHeaderCheckBoxLoaded(object sender, RoutedEventArgs e)
        {
            _headerCheckBox = sender as CheckBox;
            _headerCheckBox.Loaded -= OnHeaderCheckBoxLoaded;
            _headerCheckBox.Checked += (s2, e2) => SetAllSelectedStates(true);
            _headerCheckBox.Unchecked += (s2, e2) => SetAllSelectedStates(false);
        }

        private void OnLoadingRow(object sender, DataGridRowEventArgs e)
        {
            object dataContext = e.Row.DataContext;
            FrameworkElement element = this.GetCellContent(e.Row);
            element.DataContext = GetMarkObject(dataContext);
        }

        private void SetAllSelectedStates(bool value)
        {
            if (_ownerDataGrid.ItemsSource == null)
                return;

            var enu = _ownerDataGrid.ItemsSource.GetEnumerator();
            while (enu.MoveNext())
            {
                GetMarkObject(enu.Current).IsSelected = value;
            }
            ClearItems();
        }

        private MarkObject GetMarkObject(Object obj)
        {
            if (_markObjects.ContainsKey(obj) == false)
            {
                MarkObject markObject;
                markObject = new MarkObject();
                _markObjects.Add(obj, markObject);
                markObject.PropertyChanged += (s, e) =>
                {
                    if (e.PropertyName == "IsSelected")
                    {
                        if (SelectedItemChanged != null)
                        {
                            SelectedItemChanged(this, EventArgs.Empty);
                        }
                    }
                };
            }

            return _markObjects[obj];
        }
    }

 

其中MarkObject是一个继承INotifyPropertyChanged的类,包含Selected属性,这样更改IsSelected时可以更新UI。

最后加上DataGridExtensions 是为了方便调用。

 


 

View Code
  public static class DataGridExtensions
    {
        internal static DataGridSelectColumn GetSelectColumn(this DataGrid dataGrid)
        {
            DataGridSelectColumn result = null;
            for (int i = 0; i < dataGrid.Columns.Count; i++)
            {
                result = dataGrid.Columns[i] as DataGridSelectColumn;
                if (result != null)
                    break;
            }
            return result;
        }

        public static void EnableSelect(this DataGrid dataGird)
        {
            var column = GetSelectColumn(dataGird);
            column.OwnerDataGrid = dataGird;
        }

        public static void SelectAll(this DataGrid dataGrid)
        {
            DataGridSelectColumn column = GetSelectColumn(dataGrid);
            if (column == null)
                throw new Exception("No Select Column");
            column.SelecteAll();
        }

        public static void UnselectAll(this DataGrid dataGrid)
        {
            DataGridSelectColumn column = GetSelectColumn(dataGrid);
            if (column == null)
                throw new Exception("No Select Column");
            column.UnselectAll();
        }

        public static List<T> GetSelectedItems<T>(this DataGrid dataGrid)
        {
            DataGridSelectColumn column = GetSelectColumn(dataGrid);
            if (column == null)
                throw new Exception("No Select Column");
            return column.GetSelectedItems<T>();
        }

        public static void SetSelectedItems(this DataGrid dataGrid, IList items)
        {
            DataGridSelectColumn column = GetSelectColumn(dataGrid);
            if (column == null)
                throw new Exception("No Select Column");
            column.SetSelectedItems(items);
        }
    }

 

附上完整源码,欢迎指正。

/Files/dino623/DataGridSelectSample.zip

posted @ 2012-01-22 23:53 dino623 阅读(1117) 评论(8) 编辑

  Silverlight不提供自定义鼠标,它只提供了默认的几种鼠标样式:

  http://msdn.microsoft.com/zh-cn/library/system.windows.input.cursor(VS.95).aspx

  如果需要自定义鼠标样式,通常的做法是设置Cursor=Cursors.None,然后在最顶层放一张图片,在MouseMove事件中移动这张图片。至于调用方法,用附加属性比较方便:

 

<local:CursorEx.CustomCursor>
   
<Image Source="images/arrow.png" Height="15" Width="15"/>
</local:CursorEx.CustomCursor>

 

 

  要实现自定义鼠标,需要先理解以下几种概念:

1,Popup: Silverlight 内容区域的界限之内、现有 Silverlight 内容之上显示内容。

  Popup这东西现在是很少用了,不过在Silverlight 2 RC以前是没有ComboBox的,那时候通常就用Popup自己做一个ComboBox。现在有了ComboBox,我们也就不必在自己写了,不过ComboBox的下拉菜单还是用Popup实现的。

  使用很简单:

 

代码
 private void OnMouseMove(object sender, MouseEventArgs e)
 {
      FrameworkElement element 
= sender as FrameworkElement;
      GeneralTransform generalTransform 
= element.TransformToVisual(App.Current.RootVisual);
      Point point 
=generalTransform.Transform(e.GetPosition(element));
      Popup  pp 
= point.X;
      pp.VerticalOffset 
= _ point.Y;
      
//pp.Child=…….
      pp.IsOpen =True;
  }

 

 

 

2, public bool CaptureMouse():将鼠标捕获设置为 UIElement 

  这个方法通常用在鼠标拖动,譬如在TextBox中拖动选中文字,或是拖动滚动条。在鼠标按下时执行这个方法,如果没有其它元素已经捕获了鼠标,则返回True,并且无论鼠标移动到哪里都可以接收鼠标输入,直到执行ReleaseMouseCapture()释放鼠标。

  当捕获了鼠标后,即使鼠标在其他控件上移动,那些控件都是没有反应的。而且鼠标样式还是捕获鼠标的FrameworkElement的样式(具体可参考Window中拖动边框修改窗口大小时的鼠标样式,拖动时无论移动到哪里,鼠标样式都是不变的)。

  因为同一时间只有一个FrameworkElement可以捕获鼠标,所以可以用下面这个方法检测是否已经捕获了鼠标:

 

代码
private static bool CheckIsCapturing(FrameworkElement element)
        {
            
bool isRootCapturingMouse = App.Current.RootVisual.CaptureMouse();
            App.Current.RootVisual.ReleaseMouseCapture();
            
if (isRootCapturingMouse)
                
return false;
            
else
            {
                
if (element.CaptureMouse())
                    
return true;
                
else
                    
return false;
            }
        }

 

 

 

3VisualTreeHelper.FindElementsInHostCoordinates(Point, UIElement): 检索一组对象,这些对象位于某一对象的坐标空间的指定点内。

  依序返回点中的UIElement及其Parent,一直到Parent==Null为止,其结果是一个IEnumerable<UIElement>。因为MouseEventArgs不具备Handled属性,所以在VisualTree上同一个区域的所有UIElement都会发生MouseMove事件,其次序是从上层直到下层。如果有一个Grid,里面包含一个Border,且GridBorder都设定了自定义鼠标,则会发生冲突,所以我使用了这个方法获取最上层并且设定了自定义鼠标的UIElement 

 

 

  具体内容不一一列出,最终效果如下(借用了这里的鼠标样式http://www.cnblogs.com/gnielee/archive/2010/01/15/1648832.html):

 

   获取 Microsoft Silverlight

 

  效率好像不怎么好,而且有两个问题:

  1TextBox的鼠标是设置在ControlTemplate中的某个元素,所以在外面设Cursor=Cursors.None是没用的,而且拖动选中文字时会捕获鼠标,这也是我们不能控制的,为免同时出现默认的鼠标和自定义鼠标,特地多添加了一个附加属性“UseOriginalCursor”,设为True时只使用默认鼠标。不过,其实也是可以定义TextBox ControlTemplate改变里面的鼠标样式的。

   2:自定义鼠标出现的地点基于附加了CustomCursor属性的UIElementMouseMove事件时产生的Point,所以如果鼠标没有在CustomCursor上移动过,自定义鼠标就不会出现。

 

源代码:http://files.cnblogs.com/dino623/CursorTest100326.rar

posted @ 2010-04-01 22:20 dino623 阅读(1947) 评论(6) 编辑

  今天看到這篇文章,即模仿京東(360buy)浏览产品图片的效果:

  silverlight图片局部放大效果

  一時技痒于是也打算做一个出来。因为功能简单不如索性用纯xaml做,不写一行代码。当然,MouseDragElementBehavior这种东西也是要用到的,但这也不算是“我”写了代码吧。
  原以为会很简单,结果失败了。MouseDragElementBehavior有一个属性是ConstrainToParentBounds,即是否只在父容器的范围中拖动。这个属性理所当然地应该设置为True,但实际上只要鼠标一移出父容器被拖动的控件就完全不动了。实际效果如下,为了方便比较做了 ConstrainToParentBounds="False" 和 ConstrainToParentBounds="True"两种。 这个东西做出来的效果不是很理想,难登大雅之堂,如果需要实际使用还是老老实实地用上面那篇文章介绍的方法吧。

获取 Microsoft Silverlight

  稍微解释一下:放大区域的位移量应该和那块半透明方块的位移量成反比的,因为不想写代码,所以将放大区域反转再反转,然后再将半透明方块的位移量和放大区域的位移量绑定。MouseDragElementBehavior是通过改变半透明方块的RenderTransform来改变它的位移量,所以应该绑定RenderTransform:


  RenderTransform="{Binding RenderTransform,Mode=TwoWay,ElementName=image}"


  另外,要遮挡图片的其它部分,我倾向于直接使用ScrollViewer,不过放大区域是圆形或其它形状那就另当别论了。
  下面是全部源代码

 

 

xaml
 <Grid x:Name="LayoutRoot">
        
<Grid.Background>
            
<LinearGradientBrush EndPoint="0.189,0.967" StartPoint="0.181,0.075">
                
<GradientStop Color="#FFB3B3B3" Offset="0"/>
                
<GradientStop Color="#FFD2D2D2" Offset="1"/>
            
</LinearGradientBrush>
        
</Grid.Background>
        
<Grid.ColumnDefinitions>
            
<ColumnDefinition Width="Auto"/>
            
<ColumnDefinition/>
        
</Grid.ColumnDefinitions>
        
<StackPanel>
            
<StackPanel Orientation="Horizontal">
                
<TextBlock Text="width:" Margin="5,0" Width="40"/>
                
<Slider  Maximum="1024" x:Name="sliderX" Value="500" Width="200"
                         ToolTipService.ToolTip
="{Binding Value,ElementName=sliderX}"/>
            
</StackPanel>
            
<StackPanel Orientation="Horizontal">
                
<TextBlock Text="height:" Margin="5,0" Width="40"/>
                
<Slider  Maximum="768" x:Name="sliderY" Value="400" Width="200"  
                         ToolTipService.ToolTip
="{Binding Value,ElementName=sliderY}"/>
            
</StackPanel>
            
<TextBlock Margin="5,0" Text="ConstrainToParentBounds='False'"/>
            
<controlsToolkit:Viewbox HorizontalAlignment="Left" Margin="5,0,5,5"
                    Width
="250" >
                
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
                    
<Image Source="Penguins.jpg"/>
                    
<Rectangle Height="{Binding Value,Mode=OneWay,ElementName=sliderY}"
                               Width
="{Binding Value,Mode=OneWay,ElementName=sliderX}" 
                               x:Name
="rec" RenderTransform="{Binding RenderTransform,Mode=TwoWay,ElementName=image}"
                                   HorizontalAlignment
="Left" VerticalAlignment="Top"
                               Fill
="#66FFFFFF" Stroke="Red" StrokeThickness="2" 
                               RenderTransformOrigin
="0.5,0.5">
                        
<i:Interaction.Behaviors>
                            
<il:MouseDragElementBehavior  ConstrainToParentBounds="False" />
                        
</i:Interaction.Behaviors>

                    
</Rectangle>
                
</Grid>
            
</controlsToolkit:Viewbox>

            
<TextBlock Margin="5,0" Text="ConstrainToParentBounds='True'"/>
            
<controlsToolkit:Viewbox HorizontalAlignment="Left" Margin="5"
                    Width
="250" >
                
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
                    
<Image Source="Penguins.jpg"/>


                    
<Rectangle  RenderTransform="{Binding RenderTransform,Mode=TwoWay,ElementName=image}"
                           Height
="{Binding Value,Mode=OneWay,ElementName=sliderY}"
                                Width
="{Binding Value,Mode=OneWay,ElementName=sliderX}" x:Name="rec2"
                                HorizontalAlignment
="Left" VerticalAlignment="Top" Fill="#66FFFFFF" Stroke="Red"
                                StrokeThickness
="2" RenderTransformOrigin="0.5,0.5">
                        
<i:Interaction.Behaviors>
                            
<il:MouseDragElementBehavior ConstrainToParentBounds="True" />
                        
</i:Interaction.Behaviors>
                    
</Rectangle>
                
</Grid>
            
</controlsToolkit:Viewbox>
        
</StackPanel>

        
<controlsToolkit:Viewbox Margin="5" Padding="0" Grid.Column="1">
            
<ScrollViewer  Height="{Binding Value,Mode=OneWay,ElementName=sliderY}"
                           Width
="{Binding Value,Mode=OneWay,ElementName=sliderX}"  
                           Padding
="0" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
                
<Grid  HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="0,0" Margin="0">
                    
<Grid.RenderTransform>
                        
<TransformGroup>
                            
<ScaleTransform ScaleX="-1" ScaleY="-1"/>
                            
<SkewTransform AngleX="0" AngleY="0"/>
                            
<RotateTransform Angle="0"/>
                            
<TranslateTransform/>
                        
</TransformGroup>
                    
</Grid.RenderTransform>
                    
<Image x:Name="image" Source="Penguins.jpg"    RenderTransformOrigin="0,0" Margin="0">
                        
<Image.Projection>
                            
<PlaneProjection CenterOfRotationX="0" CenterOfRotationY="0" RotationY="180" RotationX="180"/>
                        
</Image.Projection>
                    
</Image>
                
</Grid>
            
</ScrollViewer>
        
</controlsToolkit:Viewbox>
    
</Grid>

 

 

posted @ 2009-12-03 15:37 dino623 阅读(2912) 评论(13) 编辑
摘要: 来学习WriteableBitmap吧。看看參考文檔中的描述:使用 WriteableBitmap 类基于每个框架来更新和呈现位图。这对于拍摄正播放视频的快照、生成算法内容(如分形图像)和数据可视化(如音乐可视化应用程序)很有用。SL3新增的功能中这个还算比较重要,它继承BitmapSource,使用构造函数WriteableBitmap(UIElement,Transform)可以将传入的UIE...阅读全文
posted @ 2009-09-04 18:30 dino623 阅读(1642) 评论(4) 编辑
      既然已经开始做SL3项目,而且document也已经入手了,那就开始学习吧。虽然直接读document比较方便,但谢谢学习心得总是好事。
      首先应该看看Silverlight 3 中的新增功能,Silverlight中重大的改进之一就是3D效果了,以前宣称"FLASH杀手"却连3D都没有真是难以置信的厚脸皮(还是说其实FLASH的3D也是模拟出来的?)。先来看看效果吧,是参考Silverlight 3 Beta 新特性解析(2)-Graphics篇 - 笑看风云淡 后做出来的:
获取 Microsoft Silverlight

XAML

(题外话,新增的Element-To-Element Binding功能很方便,以前使用Slider需要在ValueChanged事件中写代码实现,而且也不能这样个绑定ToolTip。)
      稍微注释一下:


  • LocalOffsetX 沿旋转对象平面的 x 轴平移对象。

  • LocalOffsetY 沿旋转对象平面的 y 轴平移对象。

  • LocalOffsetZ 沿旋转对象平面的 z 轴平移对象。

  • GlobalOffsetX 沿屏幕对齐的 x 轴平移对象。

  • GlobalOffsetY 沿屏幕对齐的 y 轴平移对象。

  • GlobalOffsetZ 沿屏幕对齐的 z 轴平移对象。

  • CenterOfRotationX、CenterOfRotationY、CenterOfRotationZ则是旋转中心点


          感觉上三维效果可以实现过去大部分的2D效果。其他的也不需要多说了吧,A silverlight is worth a thousand words.


     

  • posted @ 2009-08-29 17:19 dino623 阅读(823) 评论(0) 编辑
    摘要: 用Silverlight2整整一年了,上个星期公司全面转去Silverlight3,作为纪念就把用SL2写的最后一个东西发出来吧。效果如下:只是一个很简单的程序,甚至懒得写到Generic.xaml中,直接使用UserControl。而用ListBox做也是为了图方便,ListBox中GetContainerForItemOverride()方法能很方便地将Object转换成DependencyO...阅读全文
    posted @ 2009-08-26 18:23 dino623 阅读(690) 评论(8) 编辑