如何在DataContext略有不同的情况下重用UserControl?

时间:2013-11-11 06:55:45

标签: c# wpf mvvm user-controls datacontext

这是我在StackOverflow上的第一篇文章,也是我的第一个问题。 我在WPF中使用MVVM模式(称为Block)创建了一个UserControl。 此UserControl应该在另一个UserControl中使用,作为ListBox的DataTemplate(称为Sector)。

块控件应作为独立使用,并作为DataTemplate用于不同的UserControl。

如果我在Block of Block中设置DataContext,那么独立版本可以很好地工作,但不能作为扇区视图的一部分。

如果我没有在Block of Block中设置DataContext,它可以在Sector中工作,但不能单独使用。

我的问题是,它是将DataContext保留在Block of Block中并在View中使用Control或ViewModel进行设置的唯一方法吗?

这是我的代码:

阻止模型:

public class BlockModel : ModelBase
{
    #region private Variables

    string blockName = "Block";
    string blockContent = "00000";

    #endregion private Variables

    #region Properties

    public string BlockName
    {
        get { return blockName; }
        set { blockName = value; NotifyPropertyChange("BlockName "); }
    }

    public string BlockContent
    {
        get { return blockContent; }
        set { blockContent = value; NotifyPropertyChange("BlockContent"); }
    }

    #endregion Properties

    #region ctor

    public BlockModel () { }

    #endregion ctor
}

阻止的ViewModel:

public class BlockViewModel : ViewModelBase
{
    #region private Variables

    string charToFill = "0";
    DelegateCommand fillWithChar;
    BlockModel dataModel = new BlockModel();

    #endregion private Variables

    #region Properties

    public Models. BlockModel DataModel
    {
        get { return dataModel; }
        set { dataModel = value; }
    }

    public string BlockContent
    {
        get { return dataModel. BlockContent; }
        set
        {
            if (dataModel. BlockContent != value)
            {
                dataModel. BlockContent = value;
                NotifyPropertyChange ("BlockContent");
            }
        }
    }

    public string BlockName
    {
        get { return dataModel. BlockName; }
        set
        {
            if (dataModel. BlockName != value)
            {
                dataModel. BlockName = value;
                NotifyPropertyChange("BlockName");
            }
        }
    }


    public string CharToFill
    {
        get { return charToFill; }
        set
        {
            if (charToFill != value)
            {
                charToFill = value;
                NotifyPropertyChange ("CharToFill");
            }
        }
    }


    public ICommand FillWithChar
    {
        get
        {
            if (fillWithChar == null)
                fillWithChar = new DelegateCommand(FillText);
            return fillWithChar;
        }
    }

    #endregion Properties

    #region ctor

    public BlockViewModel()
    {

    }

    #endregion ctor

    #region Methods

    private void FillText()
    {
        CodingBlockContent = CodingBlockContent.PadLeft(32, charToFill[0]);
    }

    #endregion Methods

}

阻止视图:

<UserControl x:Class="…./Block"
         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:local="clr-namespace:…/Controls"
         mc:Ignorable="d" d:DesignWidth="460" d:DesignHeight="44">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="124" />
        <ColumnDefinition Width="301" />
        <ColumnDefinition Width="30"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="30"></RowDefinition>

    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding BlockName}" 
               HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="14" Margin="2,6"/>
    <ComboBox FontFamily="Consolas" Grid.Row="00" Grid.Column="1" 
              Text="{Binding BlockContent, Mode=TwoWay}" 
              Tag="{Binding BlockName}" Name="cbxBlock" FontSize="14" 
              HorizontalAlignment="Left" VerticalAlignment="Center" Width="290" Margin="1,4,0,5" IsEditable="True" Height="26">
        <ComboBoxItem Content=""></ComboBoxItem>
        <ComboBoxItem Content="0000000000000"></ComboBoxItem>
        <StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal">
            <TextBox Margin="10,2,0,2" Text="0" Width="24" FontSize="14" Name="tbxChar" MaxLength="1"></TextBox>
            <TextBlock Margin="10,2,0,2" Text="auffüllen" VerticalAlignment="Center" FontSize="14"></TextBlock>
            <Button Margin="10,2,0,2" Content="Ausführen" Width="100"  Command="{Binding FillWithChar}"></Button>
        </StackPanel>
    </ComboBox>
    <TextBlock Grid.Row="0" Width="30" Grid.Column="2" Text="{Binding CodingBlockContent.Length}" 
               HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="14" Margin="2,6,0,6" Grid.ColumnSpan="2"/>
</Grid>

行业模型:

public class SectorModel
{
    #region private Variables

    int blockAmount = 4;
    string sectorNumber = "Sektor";
    int blockBegin = 0;
    bool isSectorInUse = false;

    #endregion private Variables

    #region Properties

    public string SectorNumber
    {
        get { return sectorNumber; }
        set { sectorNumber = value;}
    }

    public int BlockBegin
    {
        get { return blockBegin; }
        set
        {
            blockBegin = value;
        }
    }

    public bool IsSectorInUse
    {
        get { return isSectorInUse; }
        set { isSectorInUse = value;}
    }

    public int BlockAmount
    {
        get { return blockAmount; }
        set
        {
            blockAmount = value;;
        }
    }

    #endregion Properties

  public SectorModel()
    {
    }
}

部门的ViewModel:

public class SectorViewModel : ViewModelBase
{
    #region private Variables

    SectorModel dataModel = new SectorModel();
    ObservableCollection<BlockViewModel> sectorBlocks = new ObservableCollection<BlockViewModel>();

    #endregion private Variables

    #region Properties

    public SectorModel DataModel
    {
        get { return dataModel; }
        set { dataModel = value; }
    }

    public ObservableCollection<BlockViewModel> SectorBlocks
    {
        get { return sectorBlocks; }
        set { sectorBlocks = value; NotifyPropertyChange ("SectorBlocks"); }
    }

    public string SectorNumber
    {
        get { return "Sektor " + DataModel.SectorNumber; }
        set { DataModel.SectorNumber = value; NotifyPropertyChange ("SectorNumber"); }
    }

    public int BlockBegin
    {
        get { return DataModel.BlockBegin; }
        set
        {
            DataModel.BlockBegin = value;
            SetBlocks();
            OnPropertyChanged("BlockBegin");
        }
    }

    public bool IsSectorInUse
    {
        get { return DataModel.IsSectorInUse; }
        set { DataModel.IsSectorInUse = value; NotifyPropertyChange ("IsSectorInUse"); }
    }

    public int BlockAmount
    {
        get { return DataModel.BlockAmount; }
        set
        {
            DataModel.BlockAmount = value;
            SetBlocks();
            NotifyPropertyChange ("CodingBlockAmount");
        }
    }

    #endregion Properties

    void SetBlocks()
    {
        while (SectorBlocks.Count != BlockAmount)
        {
            SectorBlocks.Add(new BlockViewModel());
        }

        int begin = BlockBegin;

        foreach (BlockViewModel block in SectorBlocks)
        {
            block.CodingBlockName = "Block " + begin.ToString().PadLeft(2, '0');
            block++;
        }
    }

    public SectorViewModel() 
    {
        SetBlocks();
    }
}

行业观点:

<UserControl xmlns:Views="clr-namespace:…/RFIDControls"  x:Class="…/Sector"
        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" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="473">
<Border BorderBrush="Black" BorderThickness="0,0,0,1">
    <Expander Name="expMain" Margin="0,0,4,0">
        <Expander.Header>
            <Grid Name="grdHeader">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100" />
                    <ColumnDefinition Width="300" />
                    <ColumnDefinition Width="30" />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Grid.Row="0" Height="24" Text="{Binding SectorNumber}" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="14" FontWeight="Bold" Panel.ZIndex="98"/>
                <CheckBox Grid.Column="2" VerticalAlignment="Center" IsChecked="{Binding IsSectorInUse}"></CheckBox>
            </Grid>
        </Expander.Header>
        <Grid>
            <ListBox ItemsSource="{Binding SectorBlocks}" Name="listBox">
                <ListBox.ItemTemplate>
                    <DataTemplate DataType="{x:Type Views:BlockViewModel}">
                        <Views:Block/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Expander>
</Border>

谢谢。

来自德国的亲切问候

多米尼克

PS:我希望我的英语不像我想的那么糟糕

2 个答案:

答案 0 :(得分:1)

这是WPF新用户的常见问题。您有两种可能的解决方案。使用MVVM时,您无需在任何地方设置DataContext的{​​{1}}。相反,您只需向UserControl添加DataTemplate即可进行配对:

App.xaml

通过这种方式,每当您展示<DataTemplate DataType="{x:Type ViewModels:BlockViewModel}"> <Views:BlockView /> </DataTemplate> 的实例时,框架都会显示相关的BlockViewModel(就像您在BlockView课程中所做的那样)。

替代选项是使用MVVM代替SectorView(使用UserControl代替),设置其{{1在内部并在内部元素上使用Bindable DependencyProperty代替:

DataContext

使用此方法,RelativeSource Binding将查看当前设置为<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding DataContext.BlockName, RelativeSource={RelativeSource AncestorType={ x:Type YourXmlNamespacePrefix:BlockView}}}" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="14" Margin="2,6"/> 的任何对象。

答案 1 :(得分:0)

听起来像Block作为一个控件,它似乎参与其他用户控件或模板的一部分,在我看来,mvvm在这里不起作用,你应该使用Dependency属性。 原谅我可怜的英语。