Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何创建具有非线性比例的滑块?_C#_.net_Wpf - Fatal编程技术网

C# 如何创建具有非线性比例的滑块?

C# 如何创建具有非线性比例的滑块?,c#,.net,wpf,C#,.net,Wpf,我有一个最小值为0,最大值为500的滑块 我想当滑块移动到100,拇指在滑块中间。 我知道这看起来很奇怪,但有些程序使用缩放滑块来实现,我相信这会更好。让滑块保持原样,并使用值转换器进行绑定。在ValueConverter中,使用非线性缩放来按您的意愿缩放值。这是一个非常有趣的问题,我不能就此置之不理,希望我能正确回答您的问题:) 你想通过在拇指在中间时指定函数的y值,将滑块< /代码>的< 值从线性变为二次函数。 二次函数写在形式上 因为我们有3个点,我们有3组X和Y的值 (X1, Y1)

我有一个最小值为0,最大值为500的滑块

我想当滑块移动到100,拇指在滑块中间。


我知道这看起来很奇怪,但有些程序使用缩放滑块来实现,我相信这会更好。

让滑块保持原样,并使用值转换器进行绑定。在ValueConverter中,使用非线性缩放来按您的意愿缩放值。

这是一个非常有趣的问题,我不能就此置之不理,希望我能正确回答您的问题:)

你想通过在拇指在中间时指定函数的y值,将<代码>滑块< /代码>的< <代码>值<代码>从线性变为二次函数。 二次函数写在形式上

因为我们有3个点,我们有3组X和Y的值

(X1, Y1) = 0, 0  
(X2, Y2) = MiddleX, CenterQuadraticValue (in your case 100)  
(X3, Y3) = Maximum, Maximum (in your case 500)
从这里,我们可以创建一个二次方程(),得出

不幸的是,此图中的某些值最终低于0,因此它们必须强制为0(我在答案的底部包含了一个图)

我创建了一个控件,
QuadraticSlider
,它源自
滑块
,并添加了两个依赖属性:
QuadraticValue
CenterQuadraticValue
。它基于
最大值
最小值
中心平方值
使用上述公式计算
平方值
。它也做相反的操作:设置
平方值
更新
。因此,不要绑定到
,而是绑定到
平方值

编辑:上一个版本有点问题。修正了一些事情

  • 当“a”为0时,从
    平方值计算
    不再中断
  • 当导数为负值时,使用了二次溶液的错误根
我上传了一个示例应用程序,其中
QuadraticSlider
用于缩放图片。可以指定所有参数,第一张图片使用
,另一张图片使用
平方值

如果你想试试的话

看起来是这样的

这就是图的样子,注意0以下的值


对梅莱克的帖子做了一些补充。 我已经稍微修正了方形滑翔机。事件处理程序存在问题(QuadraticValueChanged上的事件使用的是上一个值;初始化期间的事件使用的是超出范围的[min,max]值)


作为进一步的参考;如果您对滑块与刻度中的特定值相对应的确切位置不感兴趣,但仍然希望滑块对刻度开始处的值比结束处的值更敏感,那么使用简单的对数刻度可能就足够了

public class LogScaleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double x = (int)value;
        return Math.Log(x);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double x = (double)value;
        return (int)Math.Exp(x);
    }
}

显示值的良好公式是单调函数,如功率曲线,其形式如下:

DisplayValue = A + B * Math.Exp(C * SliderValue);
内部滑块值(例如,从0到1)通过反转公式获得:

SliderValue = Math.Log((DisplayValue - A) / B) / C;
现在如何获得A、B和C?使用您提供的三个约束条件:

f(0.0) = 0
f(0.5) = 100
f(1.0) = 500
三个方程,三个未知数,用基础数学解决:

A + B = 0
A + B exp(C * 0.5) = 100
A + B exp(C) = 500

B (exp(C * 0.5) - 1) = 100
B (exp(C) - 1) = 500

exp(C) - 5 exp(C * 0.5) + 4 = 0  // this is a quadratic equation

exp(C * 0.5) = 4

C = log(16)
B = 100/3
A = -100/3
生成以下代码:

double B = 100.0 / 3;
double C = Math.Log(16.0);
DisplayValue = B * (Math.Exp(C * SliderValue) - 1.0);
当内部值位于中间时,可以看到显示值为100:

编辑:由于请求了通用公式,因此它在这里。鉴于:

f(0.0) = x
f(0.5) = y
f(1.0) = z
A、B和C的值为:

A = (xz - y²) / (x - 2y + z)
B = (y - x)² / (x - 2y + z)
C = 2 * log((z-y) / (y-x))

请注意,如果
x-2y+z
为零,则没有解决方案,您将得到一个被零除的除法。这是因为在这种情况下,比例实际上是线性的。你需要注意这个特殊情况。

要概括Sam Hocevar的优秀答案:

将预期最大值设为M.
让滑块中点处的值为m
(显然,0
A=-M*M^2/(M^2-2*M*M)
B=M*M^2/(M^2-2*M*M)

C=Ln((M-M)^2/M^2)/基于sam hocevar的算法,以下是我编写的代码:

/// <summary>
/// Scale a linear range between 0.0-1.0 to an exponential scale using the equation returnValue = A + B * Math.Exp(C * inputValue);
/// </summary>
/// <param name="inoutValue">The value to scale</param>
/// <param name="midValue">The value returned for input value of 0.5</param>
/// <param name="maxValue">The value to be returned for input value of 1.0</param>
/// <returns></returns>
private double ExpScale(double inputValue, double midValue, double maxValue)
{
  double returnValue = 0;
  if (inputValue < 0 || inputValue > 1) throw new ArgumentOutOfRangeException("Input value must be between 0 and 1.0");
  if (midValue <= 0 || midValue >= maxValue) throw new ArgumentOutOfRangeException("MidValue must be greater than 0 and less than MaxValue");
  // returnValue = A + B * Math.Exp(C * inputValue);
  double M = maxValue / midValue;
  double C = Math.Log(Math.Pow(M - 1, 2));
  double B = maxValue / (Math.Exp(C) - 1);
  double A = -1 * B;
  returnValue = A + B * Math.Exp(C * inputValue);
  return returnValue;
}
//
///使用方程returnValue=a+B*Math.Exp(C*inputValue),将0.0-1.0之间的线性范围标度为指数标度;
/// 
///要缩放的值
///为输入值0.5返回的值
///输入值为1.0时要返回的值
/// 
专用双ExpScale(双输入值、双中值、双最大值)
{
双返回值=0;
如果(inputValue<0 | | inputValue>1)抛出新ArgumentOutOfRangeException(“输入值必须介于0和1.0之间”);
如果(中值=maxValue)抛出新ArgumentOutOfRangeException(“中值必须大于0且小于maxValue”);
//returnValue=A+B*数学表达式(C*输入值);
双M=最大值/中值;
double C=Math.Log(Math.Pow(M-1,2));
双B=最大值/(数学表达式(C)-1);
双A=-1*B;
returnValue=A+B*数学表达式(C*输入值);
返回值;
}

没有这里的其他答案那么优雅,但是你可以用两条直线代替平滑的曲线。以y为滑块位置,x为实际值,使用
(y1-y2)/(x1-x2)
获得斜率,然后
y-y1=slope*(x-x1)
获得y截距。对滑块的每一半执行此操作,然后使用条件语句根据值是高于50还是低于50来处理不同的值。您案例中的基础数学:

(0 - 50)/(0 - 100)
-50/-100
1/2
y = x/2
…因为我们知道y截距是0。然后

(50-100)/(100-500)
-50/-400
1/8
y - 50 = 1/8 * (x - 100)
y - 50 = x/8 - 25/2
y = x/8 + 75/2

从用户体验的角度来看,使用曲线(我特别喜欢)绝对是更好的选择,因为它避免了滑块突然以完全不同的速率增加值的尖锐“弯头”。但是这种方法实现简单且灵活(您可以轻松添加更多具有不同比率的区域),因此我认为无论如何都值得一提。

以下是推导指数方程的go代码:

我不知道
(0 - 50)/(0 - 100)
-50/-100
1/2
y = x/2
(50-100)/(100-500)
-50/-400
1/8
y - 50 = 1/8 * (x - 100)
y - 50 = x/8 - 25/2
y = x/8 + 75/2