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