【发布时间】:2019-05-09 21:31:07
【问题描述】:
在 WPF 中编写我的第一个项目,我无法解决闲散问题。
我有一个使用 DataSet 表中的 ItemSource 的 DataGrid(XML 中的本地 DB) 用户必须能够将列添加到 DataSet/DataGrid 并设置列 DataTemplate,例如文本、图像、日期......
所以我必须对多个列使用单个 DataTemplate,并根据列名更改绑定路径,例如:
<DataTemplate x:Key="ImageColumnTemplate">
<Grid>
<Image Source="{Binding Path=CURRENT_COLUMN_NAME Converter={StaticResource ImageReader}}" />
<TextBox Text="{Binding Path=CURRENT_COLUMN_NAME}"/>
</Grid>
</DataTemplate>
我了解这种方法不正确,但我未能找到解决方案:
- 不是基于 XAML 序列化/克隆 - 因为丢失父引用而不起作用。
-与“Path=”不同,能够将值写入行。使用继承的 DataGridBoundColumn 而不是 DataGridTemplateColumn。
DataGridTextColumn 以某种方式做到这一点,并且它有效:
Dim fGridCol = New DataGridTextColumn() With {.Header = fColumn.ColumnName}
fGridCol.Binding = New Binding(fColumn.ColumnName) With {.Mode = BindingMode.TwoWay}
但是DataGridTemplateColumn没有绑定,继承后DataGridBoundColumn不写值。
你怎样才能做到这一点?
编辑
请允许我将我的问题放在不同的背景下:
到目前为止我得到的最好的:
<Window x:Class="MainWindow"
...
<Window.Resources>
<local:CellStringReader x:Key="StringReader" />
<local:CellImageReader x:Key="ImageReader" />
<Style x:Key="TextBlockToggle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, Path=IsEditing}" Value="True">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="TextBoxToggle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridCell}, Path=IsEditing}" Value="False">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="ImageColumnTemplate">
<Grid Focusable="True">
<Grid HorizontalAlignment="Left" Background="Transparent">
<Button PreviewMouseDown="SelectImageFile" >
<Image x:Name="ImageTemplateImage" Height="20" Width="20"
Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, UpdateSourceTrigger=PropertyChanged , Converter={StaticResource ImageReader}}"/>
</Button>
</Grid>
<TextBlock x:Name="ImageTemplateTextBlock" Margin="25,0,0,0"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, UpdateSourceTrigger=PropertyChanged , Converter={StaticResource StringReader}}"/>
<TextBox x:Name="ImageTemplateTextBox" Margin="23,0,0,0" BorderThickness="0" Style="{StaticResource TextBoxToggle}"
Text="{Binding Mode=TwoWay, Path=., RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource StringReader}}"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
...
<DataGrid x:Name="LocalGrid" Grid.Row="1" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.RowValidationRules>
<local:RowDataValidationRule/>
</DataGrid.RowValidationRules>
</DataGrid>
...
</Grid>
</Window>
和
Class MainWindow
Protected Overrides Sub OnInitialized(e As EventArgs)
LocalGrid.ItemsSource = Base.Tables("Local").DefaultView
CreateColumns()
End Sub
Private WithEvents Base As New Base
Private WithEvents LocalTable As DataView = Base.Tables("Local").DefaultView
Private Sub CreateColumns()
Dim LocalTable = Base.Tables("Local")
Dim TypesTable = Base.Tables("ColumnTypes")
For Each fColumn As DataColumn In LocalTable.Columns
Dim ColumnType As String = (From fRow As DataRowView In TypesTable.DefaultView Where fRow.Item("Name") = String.Format("Local." & fColumn.ColumnName) Select fRow.Item("Template") Take 1).FirstOrDefault()
If ColumnType = "Image" Then 'THIS IS IMAGE COLUMN
Dim ImageColumn As New DataGridTemplateColumn With {.Header = fColumn.ColumnName}
ImageColumn.CellTemplate = Me.FindResource("ImageColumnTemplate")
ImageColumn.CellEditingTemplate = Me.FindResource("ImageColumnTemplate")
LocalGrid.Columns.Add(ImageColumn)
Else 'THIS IS REGILAR COLUMN
Dim fGridCol = New DataGridTextColumn() With {.Header = fColumn.ColumnName}
fGridCol.Binding = New Binding(fColumn.ColumnName) With {.Mode = BindingMode.TwoWay, .UpdateSourceTrigger = UpdateSourceTrigger.LostFocus}
LocalGrid.Columns.Add(fGridCol)
End If
Next
End Sub
Private Sub SelectImageFile(ByVal sender As Object, ByVal e As RoutedEventArgs)
'This creates OpenFileDialog on button click
End Sub
End Class
Public Class CellStringReader : Implements IValueConverter
Private EditingCell As DataGridCell
Public Overridable Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
Dim Cell As DataGridCell = value
Dim Row As DataRowView = Cell.DataContext
Dim Column As DataGridColumn = Cell.Column
If Cell.IsEditing Then
EditingCell = Cell
Else
EditingCell = Nothing
End If
Return Row.Item(Column.Header)
End Function
Public Overridable Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
If EditingCell Is Nothing Then 'This is not callded, ever.
Throw New Exception("No cell editing")
End If
Return EditingCell
End Function
End Class
Public Class CellImageReader : Inherits CellStringReader
Public Overrides Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object
value = MyBase.Convert(value, targetType, parameter, culture)
If IsDBNull(value) OrElse String.IsNullOrWhiteSpace(value) Then
Return Nothing
ElseIf IO.File.Exists(value) Then
Return New BitmapImage(New Uri(value))
End If
End Function
Public Overrides Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object
Throw New NotSupportedException
End Function
End Class
问题是,在生成的 Image 列中编辑 TextBox 不会调用 CellStringReader.ConvertBack() 并且不会写入底层 DataRow 的更改值。
我知道这是因为“Path=。”在 TextBox Binding 中,但我不知道任何替代方案。
在字符串中解析 XAML 会中断 Button PreviewMouseDown,因为缺少上下文,并且无论如何它都不会写入值。
我的问题是如何让 TextBox 在 DataRow 中写入新值?
希望现在有更多的降神会,很抱歉发了很长的帖子。
【问题讨论】:
-
一个模板列可以有任意数量的属性绑定到它里面的东西。它与文本列根本不同。如果您动态生成该列,则可以将图像的源绑定到列名,将文本框的文本绑定到列名。
-
如果您使用 mvvm 并绑定到命令,那么这是后期发现。当您使用 xamlreader.parse 创建一个绑定到命令的按钮时,它不会出错。
-
如果我明白你的意思,你怎么能这样做?
不起作用,Path=Item[ColumnName] 也不起作用。倒是你写个例子好吗? -
如果你有一个名为 Name 的列,那么你可以绑定它:
-
好的,我设法打破了我的腿并通过使用 XamlReader 解析字符串使其工作,现在我得到了按钮 PreviewMouseDown 问题和期望单击按钮:“ArgumentException:无法绑定到目标方法,因为它的签名或安全透明度与委托类型不兼容。”有任何想法吗? PS也许我应该对此提出新问题?
标签: wpf