将UserControl宽度绑定到文本框输入

时间:2018-12-13 07:16:40

标签: wpf mvvm user-controls width

这是我第一次使用MVVM pattern,在理解所有内容之间如何联系时我有些麻烦。

我有一个UserControl,带有一个Textbox元素,该元素应根据其输入来更改所述UserControl的宽度。

我面临两个问题:

  1. 为使我的想法生效,我需要更改并绑定到d:DesignWidth和我的ColumnDefinition Width。我如何以及在哪里实施这些更改?根据我对MVVM的了解,该View(在本例中为UserControl)由该UserControl的ViewModel控制。是否有必要实现一个,或者可以直接绑定到这两个属性?我知道我可以用x:Name="MyColumnDefinition"来命名ColumnDefinition,但是对于实际的UserControl Width来说也一样吗?

             mc:Ignorable="d" 
             d:DesignHeight="60" d:DesignWidth="170">
    
  2. 我有一个ObservableCollection,里面装有两个不同的UserControl,我希望在显示它们时不要重叠。我使用ListBox元素来显示ObservableCollection,并使用DataTemplateSelector在DataTemplates上实现不同的UserControls。现在可以正常使用,但是我担心如果我动态更改Control Width,它将与列表中的下一个Control重叠。我如何确保不会发生这种情况?

下面是我现在为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来容纳其他UserControls(我正在尝试构建一个可以用自定义Positioning-和DelayControls填充的Axis:

<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>

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

XZY axes with different Pos/Delay Commands

2 个答案:

答案 0 :(得分:0)

我花了很长时间才弄清楚如何解决您的问题,尽管我对结果不完全满意,但还是设法解决了。

首先,我创建一个带有DummyList的列表框,其中包含一个名为'UserControlModel'的模型对象,其属性为'modelWidth',从中创建其默认大小的UserControl。

        <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是一个RoutedEvent,用于将KeyDown-Event从我的文本框传递到我的窗口(稍后将显示) 然后,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;
}

注意: 尽管这很好用,但是如果我找到一种改变模型宽度而不是对象宽度的方法,我会更加高兴。如果重新绘制列表,其宽度仍然相同。 我还没有在UserContrl中使用MVVM模式,因此您必须像在MainWindow中一样,首先将事件从xaml.cs传递到您的视图模型中

答案 1 :(得分:-1)

选中此代码将帮助您将一个控件的宽度设置为另一控件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Dictionary<long, List<Tuple<object, Object>>> testDict = new Dictionary<long, List<Tuple<object, Object>>>();
            testDict.Add(12345 , new List<Tuple<object, object>>(){ 
                new Tuple<object, object>("developer1", "studio1"), 
                new Tuple<object, object>("developer1", "studio2"), 
                new Tuple<object, object>("developer1", "studio3") 
            });

            foreach (KeyValuePair<long, List<Tuple<object, Object>>> kvp in testDict)
            {
                Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
                foreach(Tuple<object, Object> tuple in kvp.Value)
                {
                    Console.WriteLine("Item1 = {0}, Item2 = {1}", tuple.Item1, tuple.Item2);
                }
            }
        }
    }
}