C# 引用在附着属性的静态方法中创建的对象
我正在学习WPF,并尝试创建一个完全可蒙皮和可配置的双陆棋板。 该电路板包含多个引脚,这些引脚源自网格,并包含多个椭圆和棋盘格。引脚放置在另一个网格“主网格”上,该网格基本上就是电路板。 用于创建管脚的参数作为字符串存储在资源目录中。字符串实际上是电路板的附加属性。 配置示例仅显示两个杆销的配置C# 引用在附着属性的静态方法中创建的对象,c#,wpf,C#,Wpf,我正在学习WPF,并尝试创建一个完全可蒙皮和可配置的双陆棋板。 该电路板包含多个引脚,这些引脚源自网格,并包含多个椭圆和棋盘格。引脚放置在另一个网格“主网格”上,该网格基本上就是电路板。 用于创建管脚的参数作为字符串存储在资源目录中。字符串实际上是电路板的附加属性。 配置示例仅显示两个杆销的配置 <Style x:Key="MainGrid_style" TargetType="Grid"> // ... <Setter Property="bgb:BgBoar
<Style x:Key="MainGrid_style" TargetType="Grid">
// ...
<Setter Property="bgb:BgBoard.BarParams" Value="Bottom,5,8,1,1,4/Top,5,8,6,1,4"/>
</Style>
我无法通过attached属性实例化或分配这些管脚,因为BarParamsPropertyChanged方法是静态的
因此,在我的董事会构造函数中,我添加了:
public BgBoard()
{
InitializeComponent();
_Bar[0] = (BgPin)MainGrid.FindName("Bar0");
}
但是,这不起作用,因为此指令后_Bar[0]仍然为空。你知道如何将名为Bar0的对象引用为_Bar[0]吗?任何解决方案都可以。它不需要通过“name”属性工作。您可以这样尝试: 创建一个静态事件 在构造函数中为该事件订阅一个非静态方法 在subscriber方法中将逻辑添加到阵列中,这将保证仅在创建BgPin后调用逻辑 在本例中的静态方法BarParamsPropertyChanged中,调用静态事件以便调用订户方法 在这种情况下,使类BgBoard实现IDisposable,并在Dispose方法实现中从静态事件中取消订阅非静态方法。需要此步骤来确保类的实例可以正确地进行GC-ed。 例如,未测试:
public partial class BgBoard : Window, IDisposable
{
private static event Action myEvent;
public BgBoard()
{
InitializeComponent();
myEvent += myMethod;
}
public void myMethod()
{
_Bar[0] = (BgPin)MainGrid.FindName("Bar0");
}
public void Dispose()
{
myEvent -= myMethod;
}
}
[]在代码隐藏中创建GUI元素不是WPF的设计用途。相反,您应该创建数据模型来表示电路板,然后使用数据绑定在视图中显示该数据。要了解这方面的一个好例子,请查看。我自己找到了解决方案。我在静态方法中添加了以下代码:
BgBoard Board = (BgBoard)Grid.Parent;
Board._Bar[player] = pin;
以下是完整的编码:
皮肤文件的XAML:
<Style x:Key="MainGrid_style" TargetType="Grid">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="pack://SiteOfOrigin:,,,/Graphics/WoodBoard.jpg"/>
</Setter.Value>
</Setter>
<Setter Property="bgb:BgBoard.GridRows" Value="44*,45*,45*,20*,44*,48*,44*,20*,45*,45*,44*"/>
<Setter Property="bgb:BgBoard.GridColumns" Value="36*,30*,30*,30*,30*,30*,30*,2*,30*,1*,30*,30*,30*
,30*,30*,30*,13*,33*,36*,22*,37*,4*,37*,20*,30*"/>
<Setter Property="bgb:BgBoard.BarParams" Value="Bottom,5,8,1,1,4/Top,5,8,6,1,4"/>
</Style>
XAML BgBoard
<Grid x:Name="MainGrid" Style="{DynamicResource MainGrid_style}">
代码板
public partial class BgBoard : Window
{
public static DependencyProperty BarParamsProperty = DependencyProperty.RegisterAttached("BarParams", typeof(string),
typeof(BgBoard), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(BarParamsPropertyChanged)));
public static string GetBarParams(Grid Grid) { return Convert.ToString(Grid.GetValue(BarParamsProperty)); }
public static void SetBarParams(Grid Grid, string Value) { Grid.SetValue(BarParamsProperty, Value); }
private static void BarParamsPropertyChanged(Object Sender, DependencyPropertyChangedEventArgs e)
{
Grid Grid = (Grid)Sender;
string[] sBarParams = GetBarParams(Grid).Split('/');
for (int player = (int)Player.Player1; player <= (int)Player.Player2; player++)
{
string[] sBarParam = sBarParams[player].Split(',');
string type = sBarParam[0];
int size = Convert.ToInt32(sBarParam[1]);
int column = Convert.ToInt32(sBarParam[2]);
int row = Convert.ToInt32(sBarParam[3]);
int columnspan = Convert.ToInt32(sBarParam[4]);
int rowspan = Convert.ToInt32(sBarParam[5]);
BgPin pin = new BgPin(type, size);
Grid.SetColumn(pin, column);
Grid.SetRow(pin, row);
Grid.SetColumnSpan(pin, columnspan);
Grid.SetRowSpan(pin, rowspan);
pin.Player = (Player)player;
Grid.Children.Add(pin);
BgBoard Board = (BgBoard)Grid.Parent;
Board._Bar[player] = pin;
}
}
private BgPin[] _Bar = new BgPin[2];
public BgBoard()
{
InitializeComponent();
_Bar[0].Checkers = 3;
}
这将为Player1添加3个跳棋,正如我所希望的那样。我查看了您的棋盘实现。我选择在codebehind而不是XAML中创建西洋双陆棋管脚的原因是我不想为西洋双陆棋板上的28个管脚编写重复的XAML代码—2条、24个普通管脚和2个主管脚。这28个引脚的编码完全相同。它们之间的唯一区别是播放器和pin上的跳棋数量。另外,我希望配置文件紧凑。管脚的配置定义为一个字符串:PinParams=底部,5,8,1,1,4/顶部,5,8,6,1,4/。。。而不像……我的国际象棋示例中的重复代码只是将国际象棋类型映射到URL,URL本身也可以很容易地放置在视图模型中的棋子类型或视图中的转换器类中。在任何情况下,你都不需要自己做任何类似的事情,因为你只有2件式和2针式。WPF的目标不是在XAML中完成所有工作,而是通过将视图逻辑完全排除在视图之外来保持良好的关注点分离。我仍在学习如何正确使用WPF。我读过关于使用viewmodel并完全分离模型和视图的内容。然而,我真的不知道如何为我的双陆棋板做到这一点。问题是我皮肤中的针的类型和大小是动态的。在一个蒙皮中,可以有底部和顶部销。在另一个皮肤中,所有针都可以是底部针。此外,一个大头针可以容纳的棋子数量在皮肤之间也有所不同。甚至可能每个pin都有不同的大小=它可以容纳的最大检查数。这意味着每当皮肤发生变化时,我都必须重新创建pin,因为它们的内存需求会发生变化。我能找到的唯一方法是,当皮肤发生变化时,在codebehind中重新创建PIN。我尝试过,但分配后,_Bar[0]仍然为null,因此BgPinMainGrid.FindNameBar0返回null肯定是有原因的,但我不知道具体是什么。嗯。。只是为了确保:1。在BarParamsPropertyChanged方法的末尾调用myEvent。2.发送方名称为MainGrid。如果两者都被检查过,那么我暂时没有其他想法。
<Style x:Key="MainGrid_style" TargetType="Grid">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="pack://SiteOfOrigin:,,,/Graphics/WoodBoard.jpg"/>
</Setter.Value>
</Setter>
<Setter Property="bgb:BgBoard.GridRows" Value="44*,45*,45*,20*,44*,48*,44*,20*,45*,45*,44*"/>
<Setter Property="bgb:BgBoard.GridColumns" Value="36*,30*,30*,30*,30*,30*,30*,2*,30*,1*,30*,30*,30*
,30*,30*,30*,13*,33*,36*,22*,37*,4*,37*,20*,30*"/>
<Setter Property="bgb:BgBoard.BarParams" Value="Bottom,5,8,1,1,4/Top,5,8,6,1,4"/>
</Style>
<Grid x:Name="MainGrid" Style="{DynamicResource MainGrid_style}">
public partial class BgBoard : Window
{
public static DependencyProperty BarParamsProperty = DependencyProperty.RegisterAttached("BarParams", typeof(string),
typeof(BgBoard), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(BarParamsPropertyChanged)));
public static string GetBarParams(Grid Grid) { return Convert.ToString(Grid.GetValue(BarParamsProperty)); }
public static void SetBarParams(Grid Grid, string Value) { Grid.SetValue(BarParamsProperty, Value); }
private static void BarParamsPropertyChanged(Object Sender, DependencyPropertyChangedEventArgs e)
{
Grid Grid = (Grid)Sender;
string[] sBarParams = GetBarParams(Grid).Split('/');
for (int player = (int)Player.Player1; player <= (int)Player.Player2; player++)
{
string[] sBarParam = sBarParams[player].Split(',');
string type = sBarParam[0];
int size = Convert.ToInt32(sBarParam[1]);
int column = Convert.ToInt32(sBarParam[2]);
int row = Convert.ToInt32(sBarParam[3]);
int columnspan = Convert.ToInt32(sBarParam[4]);
int rowspan = Convert.ToInt32(sBarParam[5]);
BgPin pin = new BgPin(type, size);
Grid.SetColumn(pin, column);
Grid.SetRow(pin, row);
Grid.SetColumnSpan(pin, columnspan);
Grid.SetRowSpan(pin, rowspan);
pin.Player = (Player)player;
Grid.Children.Add(pin);
BgBoard Board = (BgBoard)Grid.Parent;
Board._Bar[player] = pin;
}
}
private BgPin[] _Bar = new BgPin[2];
public BgBoard()
{
InitializeComponent();
_Bar[0].Checkers = 3;
}