使用ListView从Csv文件映射自定义字段的WPF UI

时间:2012-07-11 16:45:43

标签: wpf listview wpf-controls

您好我试图将我的应用程序迁移到WPF,并且我试图尽可能多地保留MVVM。我的WinForm应用程序的以下屏幕相当容易创建,我没有Xaml和WPF这么好运。

Screen Shot of WinForm Custom Field mapping

我喜欢它是如何工作的,但我的目标是使用WPF重新创建它,或者以某种我尚未想到的方式映射,仍然满足将输入字段映射到我的基本要求现有的数据结构。

目前这就是我在Xaml中所拥有的。

<UserControl x:Class="ImportJobDataView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:kne="clr-namespace:FatigueMVVM"
         xmlns:local="clr-namespace:FatigueMVVM.DragDropListBox"                   
         mc:Ignorable="d" d:DesignHeight="350" d:DesignWidth="447">

<Grid Height="350" Width="448">

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

    <ListView ItemsSource="{Binding ImportedJobDataColumns}"  Margin="37,68,295,64" local:DragDropHelper.IsDragSource="true">
        <ListView.View >
            <GridView AllowsColumnReorder="True"   >
                <GridViewColumn Width="100" Header="Imported"/>
            </GridView>
        </ListView.View>
    </ListView>
    <ListView ItemsSource="{Binding KneJobDataColumns}"  Margin="193,68,41,64" AllowDrop="True" local:DragDropHelper.IsDropTarget="true">
        <ListView.View >
            <GridView AllowsColumnReorder="True"   >
                <GridViewColumn Width="100" Header="Import From" DisplayMemberBinding="{Binding ImportField }"   />
                <GridViewColumn Width="100" Header="Map To" DisplayMemberBinding="{Binding  KneField }" />
            </GridView>
        </ListView.View>
    </ListView>

    <Button Content="Open Csv" Height="23" HorizontalAlignment="Left" Margin="37,15,0,0" Name="Button1" VerticalAlignment="Top" Width="75" Command="{Binding OpenCsvCommand}" Grid.Row="1" />
    <Button Content="Clean Data" Height="23" HorizontalAlignment="Left" Margin="118,15,0,0" Name="Button2" VerticalAlignment="Top" Width="auto" Grid.Row="1" />
    <Button Content="View Imported Data" Height="23" HorizontalAlignment="Left" Margin="193,15,0,0" Name="Button3" VerticalAlignment="Top" Width="auto" Grid.Row="1" />
</Grid>

这看起来很接近我希望看起来如何,但我不能拖拉工作。您可能已经注意到,我正在使用Bea Stollnitz解决方案来尝试实现拖放。 http://bea.stollnitz.com/blog/?p=53她的解决方案只能与ItemsControls一起使用,因此,我担心它不能在我用来创建两列的GridView中工作。我只使用了Listbox来尝试它,拖放功能确实有效但我真的需要两列才能使它工作。

enter image description here

是否有人建议如何在此方案中实施拖放,或者替代我目前正在尝试实现此方法的方式。

非常感谢!

2 个答案:

答案 0 :(得分:1)

我最终使用DataGrid而不是ListView,主要是因为它有更多我想要的外观。因为时间有点紧迫,因为我已经浪费了很多时间来解决这个问题,所以我决定在代码隐藏中实现拖放功能。通过阅读这个来改变我对此的看法...... http://forums.silverlight.net/t/225274.aspx/1

虽然我正在处理的情况略有不同,但现在它在代码隐藏中,有一天我或许会将它移到View-Model中以获取新的东西......如果有人不同意,我希望提供一个简单的解决方案,将其移入虚拟机,成为我的客人:)下面是UI图像,显示我正在尝试完成的任务,然后是xaml,然后是代码隐藏。我从http://www.wpftutorial.net/DragAndDrop.html开始,并从那个基础开始工作。这个功能非常特定于这个用户控件,所以虽然我对通过MVVM模式传递鼠标事件的一般概念感兴趣,但我不确定将它应用于这个案例是多么有用。

思想?

Custom Mapped Fields     

<Border BorderBrush="Silver" BorderThickness="1" Grid.RowSpan="2" HorizontalAlignment="Left" Margin="5" Name="Border1" VerticalAlignment="Top" Width="Auto" Height="Auto" >

    <StackPanel >

        <Expander Header="Instructions"  IsExpanded ="False"  ExpandDirection="Down" Width="Auto" Height="Auto" >
            <TextBlock Text="{Binding  Instructions}" TextWrapping="Wrap" Margin="5" />
        </Expander>

        <Grid Height="420" Width="485">

            <Grid.RowDefinitions >
                <RowDefinition Height="5*"  />
                <RowDefinition Height="5*"  />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="4*" />
                <ColumnDefinition Width="6*" />
            </Grid.ColumnDefinitions>

            <DataGrid AutoGenerateColumns="False" HorizontalAlignment="Stretch" 
              Margin="5" Name="DataGrid1" VerticalAlignment="Stretch"  Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"
              ItemsSource="{Binding ImportedJobDataColumns}" SelectionUnit="Cell"  CanUserReorderColumns="False"
              PreviewMouseLeftButtonDown="DataGrid_MouseLeftButtonDown" MouseMove="DataGrid_MouseMove">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Imported" Binding="{Binding Path=.}" Width="*"  IsReadOnly="True" />
                </DataGrid.Columns>
            </DataGrid>

            <DataGrid AutoGenerateColumns="False" HorizontalAlignment="Stretch" 
              Margin="5" Name="DataGrid2" VerticalAlignment="Stretch"  Grid.Column="1" Grid.Row="0"
              ItemsSource="{Binding KneJobDataColumns}" SelectionUnit="Cell" CanUserReorderColumns="False" AllowDrop="True" 
              PreviewDragOver="DataGrid_PreviewDragOver" Drop="DataGrid_Drop">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="From"  Binding="{Binding Path=ImportField}" Width="*" 
                                CanUserReorder="False" CanUserSort="False" IsReadOnly="True"/>
                    <DataGridTextColumn Header="To" Binding="{Binding KneField}" Width="*" 
                                CanUserSort="False"  CanUserReorder="False" IsReadOnly="True" />
                </DataGrid.Columns>
            </DataGrid>

            <Grid Margin="5"  VerticalAlignment="Stretch" HorizontalAlignment="Stretch"  Grid.Column="1" Grid.Row="1">

                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="2*"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <Button Content="Auto Map" Grid.Column="0" Name="Button4" Height="Auto" HorizontalAlignment="Stretch" Command="{Binding AutoMap}" />
                <Button Content="Clear Map"   Grid.Column="1" Name="Button5" Height="Auto" HorizontalAlignment="Stretch" Command="{Binding ClearMappings}"/>

                <DockPanel Grid.Row="1" Grid.Column="0" >
                    <Label Content="Max Press." HorizontalAlignment="Left" Name="Label1" VerticalAlignment="Top" Width="Auto" />
                    <xctk:IntegerUpDown  Value="{Binding MaxPressureInc}" Minimum="1" Increment="10"/>
                </DockPanel>

                <DockPanel Grid.Row="1" Grid.Column="1" >
                    <Label Content="Min Depth" Grid.Row="1" Grid.Column="1" Height="28" HorizontalAlignment="Left"  Name="Label2" VerticalAlignment="Top" Width="69" />
                    <xctk:IntegerUpDown  Grid.Row="1" Grid.Column="1" Value="{Binding MinDepthInc}" Minimum="1" Increment="1"/>
                </DockPanel>

                <Button Content="Open Csv" Grid.Row="2" Grid.Column="0" Height="Auto"  Name="Button1"   Command="{Binding OpenCsv}" />
                <Button Content="Clean Data" Grid.Row="2" Grid.Column="1" Height="Auto"  Name="Button2" Command="{Binding CleanData}"  />
                <Button Content="View Imported Data" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2"   Name="Button3"  />
                <ProgressBar  HorizontalAlignment="Stretch" Name="ProgressBar1" VerticalAlignment="Stretch" Grid.Row="4" IsIndeterminate="{Binding IsBusy}"
                 Foreground="Blue" Background="LightGray" Visibility="{Binding IsBusy, Converter={local:BooleanToVisibilityConverter}}" Grid.ColumnSpan="2" />
                <Label Content="Current File:" Grid.Column="0" Grid.Row="5" HorizontalAlignment="Left" VerticalAlignment="Center" />
                <TextBlock TextWrapping="Wrap" Text="{Binding CsvFileNameCurrent}" HorizontalAlignment="Right" Name="TextBlock1" 
                           VerticalAlignment="Center" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Width="Auto" />
            </Grid>


        </Grid>

    </StackPanel>

</Border>

Public Class ImportJobDataView

Public Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    Me.DataContext = New ImportJobDataViewModel

End Sub

Private startPoint As Point

Private Sub DataGrid_MouseLeftButtonDown(sender As System.Object, e As System.Windows.Input.MouseButtonEventArgs)
    startPoint = e.GetPosition(Nothing)
End Sub

Private Sub DataGrid_MouseMove(sender As System.Object, e As System.Windows.Input.MouseEventArgs)

    'Current Mouse Position
    Dim MousePos = e.GetPosition(Nothing)
    Dim MouseVector As Vector = startPoint - MousePos

    'Only do the following if the left moust button is held, and the cursor has moved more than the minimum horizontal distance
    If (e.LeftButton = MouseButtonState.Pressed) And (MouseVector.Length > SystemParameters.MinimumHorizontalDragDistance) Then

        If TypeOf e.OriginalSource Is TextBlock Then

            'Get the TextBlock inside the Cell
            Dim TxtBlock As TextBlock = FindAncestor(Of TextBlock)(DirectCast(e.OriginalSource, FrameworkElement))

            'Initialize the drag & drop
            Dim DragData As New DataObject("String", TxtBlock.Text)
            DragDrop.DoDragDrop(TxtBlock, DragData, DragDropEffects.Copy)
        End If

    End If

End Sub

Private Sub DataGrid_PreviewDragOver(sender As System.Object, e As System.Windows.DragEventArgs)

    If TypeOf e.OriginalSource Is TextBlock Then

        Dim Cell As DataGridCell = FindAncestor(Of DataGridCell)(DirectCast(e.OriginalSource, DependencyObject))

        'We aren't in a cell, don't allow a drop 
        If Cell Is Nothing Then
            e.Effects = DragDropEffects.None
            Exit Sub
        End If

        'Make sure we don't have a drop in the second column
        If (Cell.Column.DisplayIndex > 0) Then
            e.Effects = DragDropEffects.None
        Else
            e.Effects = DragDropEffects.Copy
        End If

    Else
        e.Effects = DragDropEffects.None
    End If

End Sub


Private Sub DataGrid_Drop(sender As System.Object, e As System.Windows.DragEventArgs)

    If e.Data.GetDataPresent("String") Then

        Dim SourceString As String = CStr(e.Data.GetData("String"))

        If TypeOf e.OriginalSource Is TextBlock Then
            'Write to cell contents only, so that we don't end up writing to the textblock inside the cells header row.
            Dim Cell As DataGridCell = FindAncestor(Of DataGridCell)(DirectCast(e.OriginalSource, DependencyObject))
            If Cell Is Nothing Then Exit Sub
            Cell.Content = SourceString
        End If

    End If

End Sub


Private Function FindAncestor(Of T As DependencyObject)(current As DependencyObject) As T
    Do
        If TypeOf current Is T Then
            Return DirectCast(current, T)
        End If
        current = VisualTreeHelper.GetParent(current)
    Loop While current IsNot Nothing
    Return Nothing
End Function

End Class

答案 1 :(得分:0)

在没有看到代码的情况下很难分辨出确切的问题,但是我之前使用过Bea Stollnitz的DragDrop代码,并且知道她的DragDrop类有一些地方可以将源或目标容器转换为{{1 }}或ItemsControl或甚至可能是ListBox,只有在结果不为空时才会继续。

您需要更新这些广告以投射为ListBoxItem(或ListView)并调整其中的逻辑以说明ListView的VisualTree(我使用Snoop来查看可视树当某事正在运行时)