绑定用户控件宽度至文本框输入

3

这是我第一次使用 MVVM模式,我有一点难以理解如何将所有内容联系在一起。

我有一个 UserControl,其中包含一个文本框元素,该元素应根据其输入更改所述UserControl的宽度。

我面临两个问题:

  1. For my idea to work, I need to change and bind to d:DesignWidth and my ColumnDefinition Width. How and where do I implement those changes? Based on my knowledge of MVVM the View (in this case my UserControl) is controlled by a ViewModel for said UserControl. Is it nessesary to implement one or is it possible to bind directly to both properties? I know I can name my ColumnDefinition with x:Name="MyColumnDefinition" but is the same possible for the actual UserControl Width?

             mc:Ignorable="d" 
             d:DesignHeight="60" d:DesignWidth="170">
    
  2. I have an ObservableCollection filled with two different UserControls and I want the Controls not to overlap when I display them. I use a ListBox element to display the ObservableCollection and implement the different UserControls over DataTemplates with a DataTemplateSelector. This works fine now but I'm worried if I dynamically change the Control Width that it will just overlap the next Control in the list. How do I ensure this won't happen?

以下是我现在为UserControl编写的代码:
<Border Background="LightGray" CornerRadius="6">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50"/>
            <ColumnDefinition Width="70"/>
            <ColumnDefinition Width="50"/>
        </Grid.ColumnDefinitions>

        <Button VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="0" 
                BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" 
                Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DeleteCommand}"
                CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DeleteCommandParameter}">
            <Rectangle Width="8" Height="8" Fill="White">
                <Rectangle.OpacityMask>
                    <VisualBrush Visual="{StaticResource appbar_close}" Stretch="Fill" />
                </Rectangle.OpacityMask>
            </Rectangle>
        </Button>

        <TextBlock Grid.Column="1" Grid.Row="0" FontSize="12" Margin="0,4,0,18" Foreground="White" HorizontalAlignment="Center" Grid.RowSpan="2">Delay</TextBlock>

        <TextBox Grid.Column="1" Grid.Row="1" Width="46" Margin="0,4,0,16" HorizontalAlignment="Center" Grid.RowSpan="2" 
                 Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=Delay.MiddlePosition, UpdateSourceTrigger=PropertyChanged}"></TextBox>

        <TextBlock Grid.Column="1" Grid.Row="2" FontSize="8" Margin="20,5,20,5" Foreground="Gray" HorizontalAlignment="Center">[s]</TextBlock>
    </Grid>
</Border>

编辑:

ListBox-XAML用于容纳其他用户控件(我正在尝试构建一个轴,可以填充自定义的定位和延迟控件:

<ListBox Name="Test" SelectionMode="Single" Grid.Column="1"
                 ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=BlockList}"
                 ItemTemplateSelector="{StaticResource BlockTemplateSelector}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="Focusable" Value="False"/>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>

最终结果应该看起来像这样,但是位置和延迟块的大小不同:

具有不同Pos/Delay命令的XZY轴


2
mc:Ignorable="d" 是一个在实时中被忽略的命名空间。它的唯一目的是在设计时修改某些内容。你可以完全删除它,没有任何影响。此外,请注意,在 WPF 网格中,您可以使用 * 和 Auto 值来设置宽度和高度。* 取用所有可用空间,Auto 取用最小所需空间。如果想要可调整大小的应用程序,请尽量避免固定宽度/高度。 - Daniele Sartori
1
请提供您列表框的XAML代码。 - Daniele Sartori
1
据我所知,DesignWidth和Height仅供设计师使用,因此更改它们不会对您的实际运行应用程序产生影响。话虽如此,您是否尝试将控件的宽度绑定到属性,并使用文本框更改它们?例如:将UserControl的宽度绑定到属性,在调用InitializeComponent之前设置属性的默认宽度,一旦确认输入(使用按钮或Enter点击事件),尝试将文本框文本解析为double,并在成功后将宽度属性更改为此值。但是,不确定这种方法的“干净程度”。 - Azzarrel
感谢 @DanieleSartori 和 @Azzarrel 提供的技巧和好主意!我将更改用户控件的固定宽度和高度。ListBox 的 XAML 已添加。 - jan97con
1
据我所知,我能想到的最简单的方法是将你的ObservableCollection "BlockList"设置为一个对象集合(如果它还不是这样)。这些对象包含了你已经拥有的所有属性,以及一个宽度属性。如果你的块是延迟类型,那么宽度是根据“MiddlePosition”属性计算出来的。然后你需要将你的边框宽度绑定到这个属性上。 - Daniele Sartori
@DanieleSartori 谢谢,是的,我也考虑过这个问题。集合已经是 Object 类型了。我会尝试给每个块一个默认宽度,并将其绑定到视图中的文本框。再次感谢! - jan97con
2个回答

0

检查这段代码将帮助您将一个控件的宽度设置为另一个控件。

<Border>
    <Grid x:Name="grv">
        <TextBox  Width="{Binding ElementName=grv,
                  Path=ActualWidth}">
            </TextBox>
    </Grid>
</Border> 

我不想让文本框改变宽度...我想让用户控件边框扩展... - jan97con
你尝试或检查过将 HorizontalAlignment="Stretch" 和 VerticalAlignment="Stretch" 应用于边框吗? - Pratius Dubey

0

我花了很长时间来解决你的问题,虽然我对结果并不完全满意,但我还是成功地解决了它。

首先,我创建了一个带有DummyList的ListBox,其中包含名为“UserControlModel”的模型对象,该对象具有单个属性“modelWidth”,然后我使用其默认大小创建了我的用户控件。

        <ListBox ItemsSource="{Binding SimpleList, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Width="Auto" Height="200">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <osv:UserControl1 Width="{Binding modelWidth}" OnTextValidated="UserControlSizeChangeEvent"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

OnTextValidated是一个路由事件,用于将我的文本框的KeyDown事件传递到我的窗口(稍后我会展示)。 UserControl1.xaml然后添加了这个文本框。

 <TextBox Width="60" Height="30" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" KeyDown="TextBox_KeyDown" Text="{Binding myText, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></TextBox>

通过一个KeyDown事件和一个文本绑定。

    private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
  if (e.Key == Key.Return)//press enter to change
  {
    if (double.TryParse(myText, out double d) == true)
    {
      if (d >= 50) //minimum width, so i won't set it to 0 by accident
      {
        myWidth = d; //set the new Width
        OnTextValidated(this, new RoutedEventArgs()); //tell window to resize the UserControl
      }

    }
  }
}

一旦我验证了新的大小既不是错误的也不是太小,我就调用一个RoutedEventHandler。

    private RoutedEventHandler m_OnTextValidated;
/// <summary>
/// 
/// </summary>
public RoutedEventHandler OnTextValidated
{
  get { return m_OnTextValidated; }
  set
  {
    m_OnTextValidated = value;
    OnPropertyChanged("CustomClick");
  }
}

现在我可以像上面展示的那样绑定它了。
接下来我要做的是将我的事件从xaml.cs传递到MinWindowViewModel。
    //Variables
    private MainWindowViewModel m_DataContext;

//Constructor
DataContext = new MainWindowViewModel ();
m_DataContext = (MainWindowViewModel)this.DataContext;



        private void UserControlSizeChangeEvent(object sender, RoutedEventArgs e)
    {
        if (m_DataContext != null)
        {
            m_DataContext.UserControlSizeChangeEvent(sender, e);
        }
    }

最后,在我的代码后端更新对象的大小

  public void UserControlSizeChangeEvent(object sender, RoutedEventArgs e)
{
  UserControl1 uc = sender as UserControl1;
  uc.Width = uc.myWidth;
}

注意:尽管这个方法运行得非常好,但如果我找到一种改变模型宽度而不是对象的方法,我会更加高兴,这样在重新绘制列表的情况下它们仍将具有相同的宽度。 我还没有为我的UserControl使用MVVM模式,因此您需要先像我为MainWindow所做的那样将事件从您的xaml.cs传递到您的viewmodel


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接