如何将GUI信息发送回数据上下文

时间:2018-08-28 14:55:18

标签: wpf multithreading powershell xaml data-binding

第一个信息,我还没有使用C#!


我最近开始在PowerShell中编程GUI,并在两周前从WinForm切换到WPF。现在,我有一个简单的问题,因此没有教程甚至不愿宣布。

如何重新获得在GUI中执行的操作的信息? 有很多教程如何绑定数据,以便将其显示在GUI中。但是现在我想以其他方式使用它:

  • 我有一个列表框,该列表框绑定到我的数据上下文的索引。
  • Textbox.Text绑定到所述列表框的选定项目。
  • 现在我想将该项目恢复到我的数据上下文中。

到目前为止,我的数据上下文定义为:

$DataContext = New-Object System.Collections.ObjectModel.ObservableCollection[Object]

并添加了一些变量

$DataContext.Add($A.VMs)
$DataContext.Add($A.VM)
.
.
.

更糟糕的是,我尝试使用本教程https://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/

在应仅用于处理我的GUI的额外线程中使用它

向文本框添加第二个绑定效果不佳。甚至没有多重绑定:

<TextBox.Text>
  <MultiBinding StringFormat="{}{0}">
     <Binding Path="SelectedItem"  ElementName ="ListBoxVM" Mode="OneWay" />
     <Binding Path="[1]" Mode="OneWayToSource" />
   </MultiBinding>  
</TextBox.Text>

我的下一个想法是在后面的代码中向datacontext [1]添加绑定,以将其绑定到TextBox.Text。但是,在此设置中,datacontext没有可以绑定的属性。


编辑:有人要求我提供更多代码,所以来了:

    Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms,System.Collections

# Creating Hashtable as base for Multithreading 
$A = [hashtable]::Synchronized(@{})

# Here is the scriptblock for the Thread containing my XAML

$GUILayerScript = {
[xml]$xaml = @"
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="VM Manager" WindowStartupLocation = "CenterScreen" Language ="de-de"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True"  Background = "Azure" ResizeMode = "NoResize" >
    <StackPanel x:Name = "StackHorz"  Margin = "5" Orientation = "Horizontal"  >
          <StackPanel x:Name = "StackLinks" Margin = "0,0,5,0" >
            <Button x:Name = "LoginButton" ToolTip = "Benutzer wechseln">
                <WrapPanel>
                    <TextBlock Text = "Logged in as: " />
                    <TextBlock x:Name ="LoginButtonText" Text = "{Binding Path=[5]}"/>
                </WrapPanel>    
            </Button>

            <ListBox x:Name = "ListBoxVM" ItemsSource = "{Binding Path=[0]}" ToolTip = "VM auswählen" MinHeight = "150" MinWidth = "100" Margin = "0,5,0,5" />
            <Button x:Name = "ButtonVMRefresh" Content ="Aktualisieren" ToolTip = "Aktualisiert die Anzeige aller VMs" />
          </StackPanel>
          <StackPanel x:Name = "StackRechts" >

            <Grid x:Name = "Grid" MinHeight = "220" MinWidth = "150" Background="azure"  Margin = "0,0,0,5" >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width = "*"/>

                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height = "Auto"/>
                    <RowDefinition Height = "Auto"/>
                    <RowDefinition Height = "Auto"/>
                    <RowDefinition Height = "Auto"/>
                    <RowDefinition Height = "Auto"/>
                    <RowDefinition Height = "Auto"/>
                </Grid.RowDefinitions>
                <Label Content = "Chosen VM:"  Grid.Column = "0" Grid.Row = "0" HorizontalAlignment="Center" VerticalAlignment="Center" />
                <TextBox x:Name = "VMChoiceText"  Grid.Column = "0" Grid.Row = "1" Height = "Auto" ToolTip = "Die aktuell Ausgewählte VM" IsReadOnly = "True" Margin = "10">
                   <TextBox.Text>
                     <MultiBinding StringFormat="{}{0}">
                        <Binding Path="SelectedItem"  ElementName ="ListBoxVM" Mode="OneWay" />
                        <Binding Path="[1]" Mode="OneWayToSource" />
                     </MultiBinding>  
                   </TextBox.Text>
                </TextBox> 
                <Label Content = "Status:" Grid.Column = "0" Grid.Row = "2" HorizontalAlignment="Center" VerticalAlignment="Center" />
                <TextBox x:Name = "StatusText" Grid.Column = "0" Grid.Row = "3"  Height = "Auto" ToolTip = "Status der VM" IsReadOnly = "True" Margin = "10,5,10,15"/>
                <Button x:Name ="ButtonVMStart" Content = "VM start" Grid.Column = "0" Grid.Row = "4" ToolTip ="Startet die ausgewähle VM"  Margin = "10"/>
                <Button x:Name ="ButtonVMStop" Content = "VM stop" Grid.Column = "0" Grid.Row = "5" ToolTip ="Fährt die ausgewähle VM herunter"  Margin = "10"/>

            </Grid>
            <TextBlock x:Name="TextBoxConsole" Height = "75" Background = "darkblue" Foreground = "white" />
          </StackPanel>
     </StackPanel>
</Window>

"@

$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )
$A.Window = $Window
$NamedElements = @( #"StackHorz",
                    #"StackLinks",
                    #"StackRechts",
                    "LoginButton",
                    "LoginButtonText",
                    #"Expander",
                    "UserTextBox",
                    "PasswordBox",
                    "LoginExec",
                    "ListBoxVM",
                   # "Grid", 
                    "ButtonVMRefresh", 
                    "TextBoxConsole", 
                    "VMChoiceText", 
                    "StatusText", 
                    "ButtonVMStart", 
                    "ButtonVMStop"

                  )
try{
$NamedElements | ForEach-Object {$A.Add("$_" , $($Window.FindName("$_")))}
}catch{}


########## Here is the DataContext set for the XAML
$A.Window.DataContext = $A.DataContext


[void]$a.Window.ShowDialog()
}

# Here I create my DataContext and give it some content
$A.VMs = 1..5
$A.MyName = $env:USERNAME

$DataContext = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
$A.DataContext = $DataContext
$DataContext.Add($A.VMs)
$DataContext.Add($A.VM)
$DataContext.Add($A.some)
$DataContext.Add($A.other)
$DataContext.Add($A.stuff)
$DataContext.Add($A.MyName)


# Thread magic is happening here

#region Thread Control
$initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$newRunspace =[runspacefactory]::CreateRunspace($initialSessionState)
$newRunspace.ApartmentState = "STA"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("A",$A) 
$newRunspace.SessionStateProxy.SetVariable("DataContext",$($A.DataContext))

$Gui = [powershell]::Create()
[void]$Gui.addscript($global:GUILayerScript)
$Gui.Runspace = $newRunspace
$GuiThread = $Gui.beginInvoke()

#endregion

1 个答案:

答案 0 :(得分:0)

目前,我使用一种变通方法,其中在codebehinde中创建一个事件处理程序,以将用户在GUI中所做的更改写回到哈希表中:

$A.VMChoiceText.add_TextChanged({$A.DataContext[1] = $A.VMChoiceText.Text})

但是我仍然愿意寻求更好的解决方案。