WPF水流动画(使用转换器模拟逻辑门控制水流信号)

前言

在使用WPF绘制流程图并模拟水流动画时,往往既需要控制阀泵的开合,又要控制动画启停。倘若能够将阀泵的开合与动画播放建立逻辑关系,这样就能够让业务代码“专心”地去控制阀泵开关,而不需要处理界面的展示。

动画示例

说明:动画中蚂蚁线的显示与隐藏是通过属性绑定配合转换器实现的。

动画解析

1、示例中【进水阀】前是一直有水流信号的,因此只要【进水阀】开,阀后即显示水流动画(此动画受一个属性控制,用单值转换器【IValueConverter】即可);

2、【进水阀】开启后,任意一个【提升泵】开启,即可出现汇入【储水池】的水流。为了与现场情况保持一致,示例中三个泵分别对应一根支管,连接到总管。水流从泵出发,通过支管流入总管,最终汇入【储水池】。

动画实现

1、水流路径处理

①当只有一个【提升泵】开启时,不用考虑路径重叠的问题,水流动画可以由一条折线来完成。

以下为单泵开启时各个泵的水流路径:

②当有多个泵开启时,只有最左边开启的泵需要绘制完整折线,其余启动泵只需绘制【支管】的水流路线。

如下,三泵全开状态。【1#泵】绘制从泵到池的完整路径,【2#泵】【3#泵】只绘制支管路径:

如下,只有【2#泵】【3#泵】开启。【2#泵】绘制从泵到池的完整路径,【3#泵】只绘制支管路径:

2、逻辑整理

①对于【1#泵】,以上两种情况下的动画路径是相同的。动画仅受【进水阀】和【1#泵】两个开关量的影响,可将其控制逻辑等效为【与门】。

②对于【2#泵】【3#泵】,【进水阀】和泵自身的开关信号是水流动画开启的必要条件,形成第一道【与门】。第二道门则根据实际情况决定:若左侧有任意泵开启,则仅绘制支管路径,此路径的第二道门为【或门】;若左侧泵全部关闭,则绘制泵到水池的完整路径,此路径的第二道门为【或非门】。

3、逻辑门实现

逻辑门借助多值转换器(IMultiValueConverter)来实现。

①与门

internal class AndGateToVisibilityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Any(a => !(bool)a))
        {
            return Visibility.Collapsed;
        }
        return Visibility.Visible;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

 ②或门

在属性绑定中,只能使用一个转换器,所以将【与门】的逻辑整合到【或门】中,需要进行第一道【与门】判定的参数索引通过ConverterParameter传入。

internal class OrGateToVisibilityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        // parameter传入的是参与第一道“与门”判定的索引,多个索引之间用逗号隔开
        List<int> andGateIndexes = parameter == null
            ? new List<int>()
            : parameter.ToString().Split(',').Select(a => System.Convert.ToInt32(a)).ToList();

        // 先进行“与门”判定
        if (andGateIndexes.Any(a => !(bool)values[a]))
        {
            return Visibility.Collapsed;
        }

        // 排除“与门”判定索引,进行“或门”判定
        for (int i = 0; i < values.Length; i++)
        {
            if (!andGateIndexes.Contains(i) && (bool)values[i])
            {
                return Visibility.Visible;
            }
        }
        return Visibility.Collapsed;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

③或非门

同样将第一道【与门】判定整合到此转换器中。

internal class NOrGateToVisibilityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        // parameter传入的是参与第一道“与门”判定的索引,多个索引之间用逗号隔开
        List<int> andGateIndexes = parameter == null
            ? new List<int>()
            : parameter.ToString().Split(',').Select(a => System.Convert.ToInt32(a)).ToList();

        // 先进行“与门”判定
        if (andGateIndexes.Any(a => !(bool)values[a]))
        {
            return Visibility.Collapsed;
        }

        // 排除“与门”判定索引,进行“或非门”判定
        for (int i = 0; i < values.Length; i++)
        {
            if (!andGateIndexes.Contains(i) && (bool)values[i])
            {
                return Visibility.Collapsed;
            }
        }
        return Visibility.Visible;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值